diff --git a/src/Umbraco.Core/Attempt{T}.cs b/src/Umbraco.Core/Attempt{T}.cs index 4a348247d4..3a6c52f840 100644 --- a/src/Umbraco.Core/Attempt{T}.cs +++ b/src/Umbraco.Core/Attempt{T}.cs @@ -1,164 +1,164 @@ -using System; -using Umbraco.Core.Dynamics; - -namespace Umbraco.Core -{ - /// - /// Represents the result of an operation attempt. - /// - /// The type of the attempted operation result. - [Serializable] - public struct Attempt - { - private readonly bool _success; - private readonly T _result; - private readonly Exception _exception; - - /// - /// Gets a value indicating whether this was successful. - /// - public bool Success - { - get { return _success; } - } - - /// - /// Gets the exception associated with an unsuccessful attempt. - /// - public Exception Exception { get { return _exception; } } - - /// - /// Gets the exception associated with an unsuccessful attempt. - /// - /// Keep it for backward compatibility sake. - [Obsolete(".Error is obsolete, you should use .Exception instead.", false)] - public Exception Error { get { return _exception; } } - - /// - /// Gets the attempt result. - /// - public T Result - { - get { return _result; } - } - - // optimize, use a singleton failed attempt - private static readonly Attempt Failed = new Attempt(false, default(T), null); - - /// - /// Represents an unsuccessful attempt. - /// - /// Keep it for backward compatibility sake. - [Obsolete(".Failed is obsolete, you should use Attempt.Fail() instead.", false)] - public static readonly Attempt False = Failed; - - // private - use Succeed() or Fail() methods to create attempts - private Attempt(bool success, T result, Exception exception) - { - _success = success; - _result = result; - _exception = exception; - } - - /// - /// Initialize a new instance of the struct with a result. - /// - /// A value indicating whether the attempt is successful. - /// The result of the attempt. - /// Keep it for backward compatibility sake. - [Obsolete("Attempt ctors are obsolete, you should use Attempt.Succeed(), Attempt.Fail() or Attempt.If() instead.", false)] - public Attempt(bool success, T result) - : this(success, result, null) - { } - - /// - /// Initialize a new instance of the struct representing a failed attempt, with an exception. - /// - /// The exception causing the failure of the attempt. - /// Keep it for backward compatibility sake. - [Obsolete("Attempt ctors are obsolete, you should use Attempt.Succeed(), Attempt.Fail() or Attempt.If() instead.", false)] - public Attempt(Exception exception) - : this(false, default(T), exception) - { } - - /// - /// Creates a successful attempt. - /// - /// The successful attempt. - public static Attempt Succeed() - { - return new Attempt(true, default(T), null); - } - - /// - /// Creates a successful attempt with a result. - /// - /// The result of the attempt. - /// The successful attempt. - public static Attempt Succeed(T result) - { - return new Attempt(true, result, null); - } - - /// - /// Creates a failed attempt. - /// - /// The failed attempt. - public static Attempt Fail() - { - return Failed; - } - - /// - /// Creates a failed attempt with an exception. - /// - /// The exception causing the failure of the attempt. - /// The failed attempt. - public static Attempt Fail(Exception exception) - { - return new Attempt(false, default(T), exception); - } - - /// - /// Creates a failed attempt with a result. - /// - /// The result of the attempt. - /// The failed attempt. - public static Attempt Fail(T result) - { - return new Attempt(false, result, null); - } - - /// - /// Creates a failed attempt with a result and an exception. - /// - /// The result of the attempt. - /// The exception causing the failure of the attempt. - /// The failed attempt. - public static Attempt Fail(T result, Exception exception) - { - return new Attempt(false, result, exception); - } - - /// - /// Creates a successful or a failed attempt. - /// - /// A value indicating whether the attempt is successful. - /// The attempt. - public static Attempt SucceedIf(bool condition) - { - return condition ? new Attempt(true, default(T), null) : Failed; - } - - /// - /// Creates a successful or a failed attempt, with a result. - /// - /// A value indicating whether the attempt is successful. - /// The result of the attempt. - /// The attempt. - public static Attempt SucceedIf(bool condition, T result) - { - return new Attempt(condition, result, null); - } - } +using System; +using Umbraco.Core.Dynamics; + +namespace Umbraco.Core +{ + /// + /// Represents the result of an operation attempt. + /// + /// The type of the attempted operation result. + [Serializable] + public struct Attempt + { + private readonly bool _success; + private readonly T _result; + private readonly Exception _exception; + + /// + /// Gets a value indicating whether this was successful. + /// + public bool Success + { + get { return _success; } + } + + /// + /// Gets the exception associated with an unsuccessful attempt. + /// + public Exception Exception { get { return _exception; } } + + /// + /// Gets the exception associated with an unsuccessful attempt. + /// + /// Keep it for backward compatibility sake. + [Obsolete(".Error is obsolete, you should use .Exception instead.", false)] + public Exception Error { get { return _exception; } } + + /// + /// Gets the attempt result. + /// + public T Result + { + get { return _result; } + } + + // optimize, use a singleton failed attempt + private static readonly Attempt Failed = new Attempt(false, default(T), null); + + /// + /// Represents an unsuccessful attempt. + /// + /// Keep it for backward compatibility sake. + [Obsolete(".Failed is obsolete, you should use Attempt.Fail() instead.", false)] + public static readonly Attempt False = Failed; + + // private - use Succeed() or Fail() methods to create attempts + private Attempt(bool success, T result, Exception exception) + { + _success = success; + _result = result; + _exception = exception; + } + + /// + /// Initialize a new instance of the struct with a result. + /// + /// A value indicating whether the attempt is successful. + /// The result of the attempt. + /// Keep it for backward compatibility sake. + [Obsolete("Attempt ctors are obsolete, you should use Attempt.Succeed(), Attempt.Fail() or Attempt.If() instead.", false)] + public Attempt(bool success, T result) + : this(success, result, null) + { } + + /// + /// Initialize a new instance of the struct representing a failed attempt, with an exception. + /// + /// The exception causing the failure of the attempt. + /// Keep it for backward compatibility sake. + [Obsolete("Attempt ctors are obsolete, you should use Attempt.Succeed(), Attempt.Fail() or Attempt.If() instead.", false)] + public Attempt(Exception exception) + : this(false, default(T), exception) + { } + + /// + /// Creates a successful attempt. + /// + /// The successful attempt. + public static Attempt Succeed() + { + return new Attempt(true, default(T), null); + } + + /// + /// Creates a successful attempt with a result. + /// + /// The result of the attempt. + /// The successful attempt. + public static Attempt Succeed(T result) + { + return new Attempt(true, result, null); + } + + /// + /// Creates a failed attempt. + /// + /// The failed attempt. + public static Attempt Fail() + { + return Failed; + } + + /// + /// Creates a failed attempt with an exception. + /// + /// The exception causing the failure of the attempt. + /// The failed attempt. + public static Attempt Fail(Exception exception) + { + return new Attempt(false, default(T), exception); + } + + /// + /// Creates a failed attempt with a result. + /// + /// The result of the attempt. + /// The failed attempt. + public static Attempt Fail(T result) + { + return new Attempt(false, result, null); + } + + /// + /// Creates a failed attempt with a result and an exception. + /// + /// The result of the attempt. + /// The exception causing the failure of the attempt. + /// The failed attempt. + public static Attempt Fail(T result, Exception exception) + { + return new Attempt(false, result, exception); + } + + /// + /// Creates a successful or a failed attempt. + /// + /// A value indicating whether the attempt is successful. + /// The attempt. + public static Attempt SucceedIf(bool condition) + { + return condition ? new Attempt(true, default(T), null) : Failed; + } + + /// + /// Creates a successful or a failed attempt, with a result. + /// + /// A value indicating whether the attempt is successful. + /// The result of the attempt. + /// The attempt. + public static Attempt SucceedIf(bool condition, T result) + { + return new Attempt(condition, result, null); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/BaseRest/BaseRestSection.cs b/src/Umbraco.Core/Configuration/BaseRest/BaseRestSection.cs index 24e7c12e03..271d07ddbb 100644 --- a/src/Umbraco.Core/Configuration/BaseRest/BaseRestSection.cs +++ b/src/Umbraco.Core/Configuration/BaseRest/BaseRestSection.cs @@ -1,38 +1,38 @@ -using System.Collections.Generic; -using System.Configuration; - -namespace Umbraco.Core.Configuration.BaseRest -{ - - internal class BaseRestSection : UmbracoConfigurationSection, IBaseRestSection - { - private const string KeyEnabled = "enabled"; - - private bool? _enabled; - - [ConfigurationProperty("", IsKey = false, IsRequired = false, IsDefaultCollection = true)] - public ExtensionElementCollection Items - { - get { return (ExtensionElementCollection)base[""]; } - } - - /// - /// Gets or sets a value indicating whether base rest extensions are enabled. - /// - [ConfigurationProperty(KeyEnabled, DefaultValue = true, IsRequired = false)] - public bool Enabled - { - get - { - return _enabled ?? (IsPresent == false || (bool)this[KeyEnabled]); - } - internal set { _enabled = value; } - } - - IExtensionsCollection IBaseRestSection.Items - { - get { return Items; } - } - - } -} +using System.Collections.Generic; +using System.Configuration; + +namespace Umbraco.Core.Configuration.BaseRest +{ + + internal class BaseRestSection : UmbracoConfigurationSection, IBaseRestSection + { + private const string KeyEnabled = "enabled"; + + private bool? _enabled; + + [ConfigurationProperty("", IsKey = false, IsRequired = false, IsDefaultCollection = true)] + public ExtensionElementCollection Items + { + get { return (ExtensionElementCollection)base[""]; } + } + + /// + /// Gets or sets a value indicating whether base rest extensions are enabled. + /// + [ConfigurationProperty(KeyEnabled, DefaultValue = true, IsRequired = false)] + public bool Enabled + { + get + { + return _enabled ?? (IsPresent == false || (bool)this[KeyEnabled]); + } + internal set { _enabled = value; } + } + + IExtensionsCollection IBaseRestSection.Items + { + get { return Items; } + } + + } +} diff --git a/src/Umbraco.Core/Configuration/BaseRest/ExtensionElement.cs b/src/Umbraco.Core/Configuration/BaseRest/ExtensionElement.cs index 9d59281b11..df785efc2b 100644 --- a/src/Umbraco.Core/Configuration/BaseRest/ExtensionElement.cs +++ b/src/Umbraco.Core/Configuration/BaseRest/ExtensionElement.cs @@ -1,81 +1,81 @@ -using System; -using System.Collections.Generic; -using System.Configuration; - -namespace Umbraco.Core.Configuration.BaseRest -{ - - [ConfigurationCollection(typeof(ExtensionElement), CollectionType = ConfigurationElementCollectionType.BasicMapAlternate)] - internal class ExtensionElement : ConfigurationElementCollection, IEnumerable, IExtension - { - const string KeyAlias = "alias"; - const string KeyType = "type"; - const string KeyMethod = "method"; - - [ConfigurationProperty(KeyAlias, IsKey = true, IsRequired = true)] - public string Alias - { - get { return (string)base[KeyAlias]; } - } - - [ConfigurationProperty(KeyType, IsKey = false, IsRequired = true)] - public string Type - { - get { return (string)base[KeyType]; } - } - - public override ConfigurationElementCollectionType CollectionType - { - get { return ConfigurationElementCollectionType.BasicMapAlternate; } - } - - protected override string ElementName - { - get { return KeyMethod; } - } - - protected override bool IsElementName(string elementName) - { - return elementName.Equals(KeyMethod, StringComparison.InvariantCultureIgnoreCase); - } - - protected override ConfigurationElement CreateNewElement() - { - return new MethodElement(); - } - - protected override object GetElementKey(ConfigurationElement element) - { - return ((MethodElement)element).Name; - } - - public override bool IsReadOnly() - { - return false; - } - - new public MethodElement this[string index] - { - get { return (MethodElement)BaseGet(index); } - } - - IEnumerator IEnumerable.GetEnumerator() - { - for (var i = 0; i < Count; i++) - { - yield return BaseGet(i) as IMethodSection; - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - IMethodSection IExtension.this[string index] - { - get { return this[index]; } - } - - } -} +using System; +using System.Collections.Generic; +using System.Configuration; + +namespace Umbraco.Core.Configuration.BaseRest +{ + + [ConfigurationCollection(typeof(ExtensionElement), CollectionType = ConfigurationElementCollectionType.BasicMapAlternate)] + internal class ExtensionElement : ConfigurationElementCollection, IEnumerable, IExtension + { + const string KeyAlias = "alias"; + const string KeyType = "type"; + const string KeyMethod = "method"; + + [ConfigurationProperty(KeyAlias, IsKey = true, IsRequired = true)] + public string Alias + { + get { return (string)base[KeyAlias]; } + } + + [ConfigurationProperty(KeyType, IsKey = false, IsRequired = true)] + public string Type + { + get { return (string)base[KeyType]; } + } + + public override ConfigurationElementCollectionType CollectionType + { + get { return ConfigurationElementCollectionType.BasicMapAlternate; } + } + + protected override string ElementName + { + get { return KeyMethod; } + } + + protected override bool IsElementName(string elementName) + { + return elementName.Equals(KeyMethod, StringComparison.InvariantCultureIgnoreCase); + } + + protected override ConfigurationElement CreateNewElement() + { + return new MethodElement(); + } + + protected override object GetElementKey(ConfigurationElement element) + { + return ((MethodElement)element).Name; + } + + public override bool IsReadOnly() + { + return false; + } + + new public MethodElement this[string index] + { + get { return (MethodElement)BaseGet(index); } + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (var i = 0; i < Count; i++) + { + yield return BaseGet(i) as IMethodSection; + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IMethodSection IExtension.this[string index] + { + get { return this[index]; } + } + + } +} diff --git a/src/Umbraco.Core/Configuration/BaseRest/ExtensionElementCollection.cs b/src/Umbraco.Core/Configuration/BaseRest/ExtensionElementCollection.cs index 0e9691606d..e941f1aa6d 100644 --- a/src/Umbraco.Core/Configuration/BaseRest/ExtensionElementCollection.cs +++ b/src/Umbraco.Core/Configuration/BaseRest/ExtensionElementCollection.cs @@ -1,70 +1,70 @@ -using System; -using System.Collections.Generic; -using System.Configuration; - -namespace Umbraco.Core.Configuration.BaseRest -{ - public interface IExtensionsCollection : IEnumerable - { - IExtension this[string index] { get; } - } - - [ConfigurationCollection(typeof(ExtensionElement), CollectionType = ConfigurationElementCollectionType.BasicMapAlternate)] - internal class ExtensionElementCollection : ConfigurationElementCollection, IExtensionsCollection - { - const string KeyExtension = "extension"; - - public override ConfigurationElementCollectionType CollectionType - { - get { return ConfigurationElementCollectionType.BasicMapAlternate; } - } - - protected override string ElementName - { - get { return KeyExtension; } - } - - protected override bool IsElementName(string elementName) - { - return elementName.Equals(KeyExtension, StringComparison.InvariantCultureIgnoreCase); - } - - protected override ConfigurationElement CreateNewElement() - { - return new ExtensionElement(); - } - - protected override object GetElementKey(ConfigurationElement element) - { - return ((ExtensionElement)element).Alias; - } - - public override bool IsReadOnly() - { - return false; - } - - new public ExtensionElement this[string index] - { - get { return (ExtensionElement)BaseGet(index); } - } - - IEnumerator IEnumerable.GetEnumerator() - { - for (var i = 0; i < Count; i++) - { - yield return BaseGet(i) as IExtension; - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - IExtension IExtensionsCollection.this[string index] - { - get { return this[index]; } - } - } -} +using System; +using System.Collections.Generic; +using System.Configuration; + +namespace Umbraco.Core.Configuration.BaseRest +{ + public interface IExtensionsCollection : IEnumerable + { + IExtension this[string index] { get; } + } + + [ConfigurationCollection(typeof(ExtensionElement), CollectionType = ConfigurationElementCollectionType.BasicMapAlternate)] + internal class ExtensionElementCollection : ConfigurationElementCollection, IExtensionsCollection + { + const string KeyExtension = "extension"; + + public override ConfigurationElementCollectionType CollectionType + { + get { return ConfigurationElementCollectionType.BasicMapAlternate; } + } + + protected override string ElementName + { + get { return KeyExtension; } + } + + protected override bool IsElementName(string elementName) + { + return elementName.Equals(KeyExtension, StringComparison.InvariantCultureIgnoreCase); + } + + protected override ConfigurationElement CreateNewElement() + { + return new ExtensionElement(); + } + + protected override object GetElementKey(ConfigurationElement element) + { + return ((ExtensionElement)element).Alias; + } + + public override bool IsReadOnly() + { + return false; + } + + new public ExtensionElement this[string index] + { + get { return (ExtensionElement)BaseGet(index); } + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (var i = 0; i < Count; i++) + { + yield return BaseGet(i) as IExtension; + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IExtension IExtensionsCollection.this[string index] + { + get { return this[index]; } + } + } +} diff --git a/src/Umbraco.Core/Configuration/BaseRest/IBaseRestSection.cs b/src/Umbraco.Core/Configuration/BaseRest/IBaseRestSection.cs index ff661465eb..24808d6b24 100644 --- a/src/Umbraco.Core/Configuration/BaseRest/IBaseRestSection.cs +++ b/src/Umbraco.Core/Configuration/BaseRest/IBaseRestSection.cs @@ -1,14 +1,14 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Configuration.BaseRest -{ - public interface IBaseRestSection - { - IExtensionsCollection Items { get; } - - /// - /// Gets a value indicating whether base rest extensions are enabled. - /// - bool Enabled { get; } - } +using System.Collections.Generic; + +namespace Umbraco.Core.Configuration.BaseRest +{ + public interface IBaseRestSection + { + IExtensionsCollection Items { get; } + + /// + /// Gets a value indicating whether base rest extensions are enabled. + /// + bool Enabled { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/BaseRest/IExtension.cs b/src/Umbraco.Core/Configuration/BaseRest/IExtension.cs index a0657d8f25..912898290b 100644 --- a/src/Umbraco.Core/Configuration/BaseRest/IExtension.cs +++ b/src/Umbraco.Core/Configuration/BaseRest/IExtension.cs @@ -1,11 +1,11 @@ -namespace Umbraco.Core.Configuration.BaseRest -{ - public interface IExtension - { - string Alias { get; } - - string Type { get; } - - IMethodSection this[string index] { get; } - } +namespace Umbraco.Core.Configuration.BaseRest +{ + public interface IExtension + { + string Alias { get; } + + string Type { get; } + + IMethodSection this[string index] { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/BaseRest/IMethodSection.cs b/src/Umbraco.Core/Configuration/BaseRest/IMethodSection.cs index c226c34a60..2c2f63928c 100644 --- a/src/Umbraco.Core/Configuration/BaseRest/IMethodSection.cs +++ b/src/Umbraco.Core/Configuration/BaseRest/IMethodSection.cs @@ -1,17 +1,17 @@ -namespace Umbraco.Core.Configuration.BaseRest -{ - public interface IMethodSection - { - string Name { get; } - - bool AllowAll { get; } - - string AllowGroup { get; } - - string AllowType { get; } - - string AllowMember { get; } - - bool ReturnXml { get; } - } +namespace Umbraco.Core.Configuration.BaseRest +{ + public interface IMethodSection + { + string Name { get; } + + bool AllowAll { get; } + + string AllowGroup { get; } + + string AllowType { get; } + + string AllowMember { get; } + + bool ReturnXml { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/BaseRest/MethodElement.cs b/src/Umbraco.Core/Configuration/BaseRest/MethodElement.cs index c52999b9ea..ab86fb2227 100644 --- a/src/Umbraco.Core/Configuration/BaseRest/MethodElement.cs +++ b/src/Umbraco.Core/Configuration/BaseRest/MethodElement.cs @@ -1,50 +1,50 @@ -using System.Configuration; - -namespace Umbraco.Core.Configuration.BaseRest -{ - internal class MethodElement : ConfigurationElement, IMethodSection - { - const string KeyName = "name"; - const string KeyAllowAll = "allowAll"; - const string KeyAllowGroup = "allowGroup"; - const string KeyAllowType = "allowType"; - const string KeyAllowMember = "allowMember"; - const string KeyReturnXml = "returnXml"; - - [ConfigurationProperty(KeyName, IsKey = true, IsRequired = true)] - public string Name - { - get { return (string)base[KeyName]; } - } - - [ConfigurationProperty(KeyAllowAll, IsKey = false, IsRequired = false, DefaultValue = false)] - public bool AllowAll - { - get { return (bool)base[KeyAllowAll]; } - } - - [ConfigurationProperty(KeyAllowGroup, IsKey = false, IsRequired = false, DefaultValue = null)] - public string AllowGroup - { - get { return (string)base[KeyAllowGroup]; } - } - - [ConfigurationProperty(KeyAllowType, IsKey = false, IsRequired = false, DefaultValue = null)] - public string AllowType - { - get { return (string)base[KeyAllowType]; } - } - - [ConfigurationProperty(KeyAllowMember, IsKey = false, IsRequired = false, DefaultValue = null)] - public string AllowMember - { - get { return (string)base[KeyAllowMember]; } - } - - [ConfigurationProperty(KeyReturnXml, IsKey = false, IsRequired = false, DefaultValue = true)] - public bool ReturnXml - { - get { return (bool)base[KeyReturnXml]; } - } - } -} +using System.Configuration; + +namespace Umbraco.Core.Configuration.BaseRest +{ + internal class MethodElement : ConfigurationElement, IMethodSection + { + const string KeyName = "name"; + const string KeyAllowAll = "allowAll"; + const string KeyAllowGroup = "allowGroup"; + const string KeyAllowType = "allowType"; + const string KeyAllowMember = "allowMember"; + const string KeyReturnXml = "returnXml"; + + [ConfigurationProperty(KeyName, IsKey = true, IsRequired = true)] + public string Name + { + get { return (string)base[KeyName]; } + } + + [ConfigurationProperty(KeyAllowAll, IsKey = false, IsRequired = false, DefaultValue = false)] + public bool AllowAll + { + get { return (bool)base[KeyAllowAll]; } + } + + [ConfigurationProperty(KeyAllowGroup, IsKey = false, IsRequired = false, DefaultValue = null)] + public string AllowGroup + { + get { return (string)base[KeyAllowGroup]; } + } + + [ConfigurationProperty(KeyAllowType, IsKey = false, IsRequired = false, DefaultValue = null)] + public string AllowType + { + get { return (string)base[KeyAllowType]; } + } + + [ConfigurationProperty(KeyAllowMember, IsKey = false, IsRequired = false, DefaultValue = null)] + public string AllowMember + { + get { return (string)base[KeyAllowMember]; } + } + + [ConfigurationProperty(KeyReturnXml, IsKey = false, IsRequired = false, DefaultValue = true)] + public bool ReturnXml + { + get { return (bool)base[KeyReturnXml]; } + } + } +} diff --git a/src/Umbraco.Core/Configuration/CommaDelimitedConfigurationElement.cs b/src/Umbraco.Core/Configuration/CommaDelimitedConfigurationElement.cs index 7cc8b68ff2..d8be9c850b 100644 --- a/src/Umbraco.Core/Configuration/CommaDelimitedConfigurationElement.cs +++ b/src/Umbraco.Core/Configuration/CommaDelimitedConfigurationElement.cs @@ -1,70 +1,70 @@ -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Configuration; - -namespace Umbraco.Core.Configuration -{ - /// - /// Defines a configuration section that contains inner text that is comma delimited - /// - internal class CommaDelimitedConfigurationElement : InnerTextConfigurationElement, IEnumerable - { - public override CommaDelimitedStringCollection Value - { - get - { - var converter = new CommaDelimitedStringCollectionConverter(); - return (CommaDelimitedStringCollection) converter.ConvertFrom(RawValue); - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return new InnerEnumerator(Value.GetEnumerator()); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return new InnerEnumerator(Value.GetEnumerator()); - } - - /// - /// A wrapper for StringEnumerator since it doesn't explicitly implement IEnumerable - /// - private class InnerEnumerator : IEnumerator - { - private readonly StringEnumerator _stringEnumerator; - - public InnerEnumerator(StringEnumerator stringEnumerator) - { - _stringEnumerator = stringEnumerator; - } - - public bool MoveNext() - { - return _stringEnumerator.MoveNext(); - } - - public void Reset() - { - _stringEnumerator.Reset(); - } - - string IEnumerator.Current - { - get { return _stringEnumerator.Current; } - } - - public object Current - { - get { return _stringEnumerator.Current; } - } - - public void Dispose() - { - ObjectExtensions.DisposeIfDisposable(_stringEnumerator); - } - } - } +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Configuration; + +namespace Umbraco.Core.Configuration +{ + /// + /// Defines a configuration section that contains inner text that is comma delimited + /// + internal class CommaDelimitedConfigurationElement : InnerTextConfigurationElement, IEnumerable + { + public override CommaDelimitedStringCollection Value + { + get + { + var converter = new CommaDelimitedStringCollectionConverter(); + return (CommaDelimitedStringCollection) converter.ConvertFrom(RawValue); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new InnerEnumerator(Value.GetEnumerator()); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new InnerEnumerator(Value.GetEnumerator()); + } + + /// + /// A wrapper for StringEnumerator since it doesn't explicitly implement IEnumerable + /// + private class InnerEnumerator : IEnumerator + { + private readonly StringEnumerator _stringEnumerator; + + public InnerEnumerator(StringEnumerator stringEnumerator) + { + _stringEnumerator = stringEnumerator; + } + + public bool MoveNext() + { + return _stringEnumerator.MoveNext(); + } + + public void Reset() + { + _stringEnumerator.Reset(); + } + + string IEnumerator.Current + { + get { return _stringEnumerator.Current; } + } + + public object Current + { + get { return _stringEnumerator.Current; } + } + + public void Dispose() + { + ObjectExtensions.DisposeIfDisposable(_stringEnumerator); + } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/AccessElement.cs b/src/Umbraco.Core/Configuration/Dashboard/AccessElement.cs index bb798467b9..da7f7e3e49 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/AccessElement.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/AccessElement.cs @@ -1,34 +1,34 @@ -using System.Collections.Generic; -using System.Linq; -using System.Xml.Linq; - -namespace Umbraco.Core.Configuration.Dashboard -{ - internal class AccessElement : RawXmlConfigurationElement, IAccess - { - public AccessElement() - { - - } - - public AccessElement(XElement rawXml) - :base(rawXml) - { - } - - public IEnumerable Rules - { - get - { - var result = new List(); - if (RawXml != null) - { - result.AddRange(RawXml.Elements("deny").Select(x => new AccessItem {Action = AccessType.Deny, Value = x.Value })); - result.AddRange(RawXml.Elements("grant").Select(x => new AccessItem { Action = AccessType.Grant, Value = x.Value })); - result.AddRange(RawXml.Elements("grantBySection").Select(x => new AccessItem { Action = AccessType.GrantBySection, Value = x.Value })); - } - return result; - } - } - } +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class AccessElement : RawXmlConfigurationElement, IAccess + { + public AccessElement() + { + + } + + public AccessElement(XElement rawXml) + :base(rawXml) + { + } + + public IEnumerable Rules + { + get + { + var result = new List(); + if (RawXml != null) + { + result.AddRange(RawXml.Elements("deny").Select(x => new AccessItem {Action = AccessType.Deny, Value = x.Value })); + result.AddRange(RawXml.Elements("grant").Select(x => new AccessItem { Action = AccessType.Grant, Value = x.Value })); + result.AddRange(RawXml.Elements("grantBySection").Select(x => new AccessItem { Action = AccessType.GrantBySection, Value = x.Value })); + } + return result; + } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/AccessItem.cs b/src/Umbraco.Core/Configuration/Dashboard/AccessItem.cs index 65ae6299d6..0daacfb17c 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/AccessItem.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/AccessItem.cs @@ -1,15 +1,15 @@ -namespace Umbraco.Core.Configuration.Dashboard -{ - internal class AccessItem : IAccessItem - { - /// - /// This can be grant, deny or grantBySection - /// - public AccessType Action { get; set; } - - /// - /// The value of the action - /// - public string Value { get; set; } - } +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class AccessItem : IAccessItem + { + /// + /// This can be grant, deny or grantBySection + /// + public AccessType Action { get; set; } + + /// + /// The value of the action + /// + public string Value { get; set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/AccessType.cs b/src/Umbraco.Core/Configuration/Dashboard/AccessType.cs index 115d416010..afc74b671f 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/AccessType.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/AccessType.cs @@ -1,9 +1,9 @@ -namespace Umbraco.Core.Configuration.Dashboard -{ - public enum AccessType - { - Grant, - Deny, - GrantBySection - } +namespace Umbraco.Core.Configuration.Dashboard +{ + public enum AccessType + { + Grant, + Deny, + GrantBySection + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/AreaCollection.cs b/src/Umbraco.Core/Configuration/Dashboard/AreaCollection.cs index 03acce069f..ac3706d0d4 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/AreaCollection.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/AreaCollection.cs @@ -1,32 +1,32 @@ -using System.Collections; -using System.Collections.Generic; -using System.Configuration; - -namespace Umbraco.Core.Configuration.Dashboard -{ - internal class AreaCollection : ConfigurationElementCollection, IEnumerable - { - protected override ConfigurationElement CreateNewElement() - { - return new AreaElement(); - } - - protected override object GetElementKey(ConfigurationElement element) - { - return ((AreaElement) element).Value; - } - - IEnumerator IEnumerable.GetEnumerator() - { - for (var i = 0; i < Count; i++) - { - yield return BaseGet(i) as IArea; - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } +using System.Collections; +using System.Collections.Generic; +using System.Configuration; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class AreaCollection : ConfigurationElementCollection, IEnumerable + { + protected override ConfigurationElement CreateNewElement() + { + return new AreaElement(); + } + + protected override object GetElementKey(ConfigurationElement element) + { + return ((AreaElement) element).Value; + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (var i = 0; i < Count; i++) + { + yield return BaseGet(i) as IArea; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/AreaElement.cs b/src/Umbraco.Core/Configuration/Dashboard/AreaElement.cs index 1f498f9eae..baea6ce2cd 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/AreaElement.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/AreaElement.cs @@ -1,12 +1,12 @@ -using System.Configuration; - -namespace Umbraco.Core.Configuration.Dashboard -{ - internal class AreaElement : InnerTextConfigurationElement, IArea - { - string IArea.AreaName - { - get { return Value; } - } - } +using System.Configuration; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class AreaElement : InnerTextConfigurationElement, IArea + { + string IArea.AreaName + { + get { return Value; } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/AreasElement.cs b/src/Umbraco.Core/Configuration/Dashboard/AreasElement.cs index 3b63a4188f..037c394799 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/AreasElement.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/AreasElement.cs @@ -1,15 +1,15 @@ -using System.Configuration; - -namespace Umbraco.Core.Configuration.Dashboard -{ - internal class AreasElement : ConfigurationElement - { - [ConfigurationCollection(typeof(SectionCollection), AddItemName = "area")] - [ConfigurationProperty("", IsDefaultCollection = true)] - public AreaCollection AreaCollection - { - get { return (AreaCollection)base[""]; } - set { base[""] = value; } - } - } +using System.Configuration; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class AreasElement : ConfigurationElement + { + [ConfigurationCollection(typeof(SectionCollection), AddItemName = "area")] + [ConfigurationProperty("", IsDefaultCollection = true)] + public AreaCollection AreaCollection + { + get { return (AreaCollection)base[""]; } + set { base[""] = value; } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/ControlCollection.cs b/src/Umbraco.Core/Configuration/Dashboard/ControlCollection.cs index 3ba0dd4c95..1b39c206c6 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/ControlCollection.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/ControlCollection.cs @@ -1,37 +1,37 @@ -using System.Collections; -using System.Collections.Generic; -using System.Configuration; - -namespace Umbraco.Core.Configuration.Dashboard -{ - internal class ControlCollection : ConfigurationElementCollection, IEnumerable - { - internal void Add(ControlElement c) - { - BaseAdd(c); - } - - protected override ConfigurationElement CreateNewElement() - { - return new ControlElement(); - } - - protected override object GetElementKey(ConfigurationElement element) - { - return ((ControlElement)element).ControlPath; - } - - IEnumerator IEnumerable.GetEnumerator() - { - for (var i = 0; i < Count; i++) - { - yield return BaseGet(i) as IDashboardControl; - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } +using System.Collections; +using System.Collections.Generic; +using System.Configuration; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class ControlCollection : ConfigurationElementCollection, IEnumerable + { + internal void Add(ControlElement c) + { + BaseAdd(c); + } + + protected override ConfigurationElement CreateNewElement() + { + return new ControlElement(); + } + + protected override object GetElementKey(ConfigurationElement element) + { + return ((ControlElement)element).ControlPath; + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (var i = 0; i < Count; i++) + { + yield return BaseGet(i) as IDashboardControl; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/ControlElement.cs b/src/Umbraco.Core/Configuration/Dashboard/ControlElement.cs index ac5c01ddeb..582998d1f2 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/ControlElement.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/ControlElement.cs @@ -1,74 +1,74 @@ -using System; -using System.Configuration; -using System.Linq; -using System.Xml.Linq; - -namespace Umbraco.Core.Configuration.Dashboard -{ - - internal class ControlElement : RawXmlConfigurationElement, IDashboardControl - { - public bool ShowOnce - { - get - { - return RawXml.Attribute("showOnce") == null - ? false - : bool.Parse(RawXml.Attribute("showOnce").Value); - } - } - - public bool AddPanel - { - get - { - return RawXml.Attribute("addPanel") == null - ? true - : bool.Parse(RawXml.Attribute("addPanel").Value); - } - } - - public string PanelCaption - { - get - { - return RawXml.Attribute("panelCaption") == null - ? "" - : RawXml.Attribute("panelCaption").Value; - } - } - - public AccessElement Access - { - get - { - var access = RawXml.Element("access"); - if (access == null) - { - return new AccessElement(); - } - return new AccessElement(access); - } - } - - public string ControlPath - { - get - { - //we need to return the first (and only) text element of the children (wtf... who designed this configuration ! :P ) - var txt = RawXml.Nodes().OfType().FirstOrDefault(); - if (txt == null) - { - throw new ConfigurationErrorsException("The control element must contain a text node indicating the control path"); - } - return txt.Value.Trim(); - } - } - - - IAccess IDashboardControl.AccessRights - { - get { return Access; } - } - } +using System; +using System.Configuration; +using System.Linq; +using System.Xml.Linq; + +namespace Umbraco.Core.Configuration.Dashboard +{ + + internal class ControlElement : RawXmlConfigurationElement, IDashboardControl + { + public bool ShowOnce + { + get + { + return RawXml.Attribute("showOnce") == null + ? false + : bool.Parse(RawXml.Attribute("showOnce").Value); + } + } + + public bool AddPanel + { + get + { + return RawXml.Attribute("addPanel") == null + ? true + : bool.Parse(RawXml.Attribute("addPanel").Value); + } + } + + public string PanelCaption + { + get + { + return RawXml.Attribute("panelCaption") == null + ? "" + : RawXml.Attribute("panelCaption").Value; + } + } + + public AccessElement Access + { + get + { + var access = RawXml.Element("access"); + if (access == null) + { + return new AccessElement(); + } + return new AccessElement(access); + } + } + + public string ControlPath + { + get + { + //we need to return the first (and only) text element of the children (wtf... who designed this configuration ! :P ) + var txt = RawXml.Nodes().OfType().FirstOrDefault(); + if (txt == null) + { + throw new ConfigurationErrorsException("The control element must contain a text node indicating the control path"); + } + return txt.Value.Trim(); + } + } + + + IAccess IDashboardControl.AccessRights + { + get { return Access; } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/DashboardSection.cs b/src/Umbraco.Core/Configuration/Dashboard/DashboardSection.cs index 12bf0522e0..4d78abcd6b 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/DashboardSection.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/DashboardSection.cs @@ -1,24 +1,24 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Text; -using System.Threading.Tasks; - -namespace Umbraco.Core.Configuration.Dashboard -{ - internal class DashboardSection : ConfigurationSection, IDashboardSection - { - [ConfigurationCollection(typeof(SectionCollection), AddItemName = "section")] - [ConfigurationProperty("", IsDefaultCollection = true)] - public SectionCollection SectionCollection - { - get { return (SectionCollection)base[""]; } - set { base[""] = value; } - } - - IEnumerable IDashboardSection.Sections - { - get { return SectionCollection; } - } - } -} +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class DashboardSection : ConfigurationSection, IDashboardSection + { + [ConfigurationCollection(typeof(SectionCollection), AddItemName = "section")] + [ConfigurationProperty("", IsDefaultCollection = true)] + public SectionCollection SectionCollection + { + get { return (SectionCollection)base[""]; } + set { base[""] = value; } + } + + IEnumerable IDashboardSection.Sections + { + get { return SectionCollection; } + } + } +} diff --git a/src/Umbraco.Core/Configuration/Dashboard/IAccess.cs b/src/Umbraco.Core/Configuration/Dashboard/IAccess.cs index 9fee2f80e1..0c275e1373 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/IAccess.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/IAccess.cs @@ -1,9 +1,9 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Configuration.Dashboard -{ - public interface IAccess - { - IEnumerable Rules { get; } - } +using System.Collections.Generic; + +namespace Umbraco.Core.Configuration.Dashboard +{ + public interface IAccess + { + IEnumerable Rules { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/IAccessItem.cs b/src/Umbraco.Core/Configuration/Dashboard/IAccessItem.cs index 7583d46306..a0ae9c2d5e 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/IAccessItem.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/IAccessItem.cs @@ -1,15 +1,15 @@ -namespace Umbraco.Core.Configuration.Dashboard -{ - public interface IAccessItem - { - /// - /// This can be grant, deny or grantBySection - /// - AccessType Action { get; set; } - - /// - /// The value of the action - /// - string Value { get; set; } - } +namespace Umbraco.Core.Configuration.Dashboard +{ + public interface IAccessItem + { + /// + /// This can be grant, deny or grantBySection + /// + AccessType Action { get; set; } + + /// + /// The value of the action + /// + string Value { get; set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/IArea.cs b/src/Umbraco.Core/Configuration/Dashboard/IArea.cs index 08775ee12f..c562c915d7 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/IArea.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/IArea.cs @@ -1,7 +1,7 @@ -namespace Umbraco.Core.Configuration.Dashboard -{ - public interface IArea - { - string AreaName { get; } - } +namespace Umbraco.Core.Configuration.Dashboard +{ + public interface IArea + { + string AreaName { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/IDashboardControl.cs b/src/Umbraco.Core/Configuration/Dashboard/IDashboardControl.cs index d5812ca5e9..c362d3ed78 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/IDashboardControl.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/IDashboardControl.cs @@ -1,15 +1,15 @@ -namespace Umbraco.Core.Configuration.Dashboard -{ - public interface IDashboardControl - { - bool ShowOnce { get; } - - bool AddPanel { get; } - - string PanelCaption { get; } - - string ControlPath { get; } - - IAccess AccessRights { get; } - } +namespace Umbraco.Core.Configuration.Dashboard +{ + public interface IDashboardControl + { + bool ShowOnce { get; } + + bool AddPanel { get; } + + string PanelCaption { get; } + + string ControlPath { get; } + + IAccess AccessRights { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/IDashboardSection.cs b/src/Umbraco.Core/Configuration/Dashboard/IDashboardSection.cs index 555c9b7439..2b1da4a869 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/IDashboardSection.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/IDashboardSection.cs @@ -1,9 +1,9 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Configuration.Dashboard -{ - public interface IDashboardSection - { - IEnumerable Sections { get; } - } +using System.Collections.Generic; + +namespace Umbraco.Core.Configuration.Dashboard +{ + public interface IDashboardSection + { + IEnumerable Sections { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/IDashboardTab.cs b/src/Umbraco.Core/Configuration/Dashboard/IDashboardTab.cs index 0a03e81da4..28cdc64021 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/IDashboardTab.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/IDashboardTab.cs @@ -1,13 +1,13 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Configuration.Dashboard -{ - public interface IDashboardTab - { - string Caption { get; } - - IEnumerable Controls { get; } - - IAccess AccessRights { get; } - } +using System.Collections.Generic; + +namespace Umbraco.Core.Configuration.Dashboard +{ + public interface IDashboardTab + { + string Caption { get; } + + IEnumerable Controls { get; } + + IAccess AccessRights { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/ISection.cs b/src/Umbraco.Core/Configuration/Dashboard/ISection.cs index 39b86717e7..48c53382c6 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/ISection.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/ISection.cs @@ -1,15 +1,15 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Configuration.Dashboard -{ - public interface ISection - { - string Alias { get; } - - IEnumerable Areas { get; } - - IEnumerable Tabs { get; } - - IAccess AccessRights { get; } - } +using System.Collections.Generic; + +namespace Umbraco.Core.Configuration.Dashboard +{ + public interface ISection + { + string Alias { get; } + + IEnumerable Areas { get; } + + IEnumerable Tabs { get; } + + IAccess AccessRights { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/SectionCollection.cs b/src/Umbraco.Core/Configuration/Dashboard/SectionCollection.cs index 395ce7f623..2f47de2af6 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/SectionCollection.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/SectionCollection.cs @@ -1,37 +1,37 @@ -using System.Collections; -using System.Collections.Generic; -using System.Configuration; - -namespace Umbraco.Core.Configuration.Dashboard -{ - internal class SectionCollection : ConfigurationElementCollection, IEnumerable - { - internal void Add(SectionElement c) - { - BaseAdd(c); - } - - protected override ConfigurationElement CreateNewElement() - { - return new SectionElement(); - } - - protected override object GetElementKey(ConfigurationElement element) - { - return ((SectionElement)element).Alias; - } - - IEnumerator IEnumerable.GetEnumerator() - { - for (var i = 0; i < Count; i++) - { - yield return BaseGet(i) as ISection; - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } +using System.Collections; +using System.Collections.Generic; +using System.Configuration; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class SectionCollection : ConfigurationElementCollection, IEnumerable + { + internal void Add(SectionElement c) + { + BaseAdd(c); + } + + protected override ConfigurationElement CreateNewElement() + { + return new SectionElement(); + } + + protected override object GetElementKey(ConfigurationElement element) + { + return ((SectionElement)element).Alias; + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (var i = 0; i < Count; i++) + { + yield return BaseGet(i) as ISection; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/SectionElement.cs b/src/Umbraco.Core/Configuration/Dashboard/SectionElement.cs index b34d2c6293..9adacd0b87 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/SectionElement.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/SectionElement.cs @@ -1,50 +1,50 @@ -using System.Collections.Generic; -using System.Configuration; -using System.Linq; - -namespace Umbraco.Core.Configuration.Dashboard -{ - internal class SectionElement : ConfigurationElement, ISection - { - [ConfigurationProperty("alias", IsRequired = true)] - public string Alias - { - get { return (string) this["alias"]; } - } - - [ConfigurationProperty("areas", IsRequired = true)] - public AreasElement Areas - { - get { return (AreasElement)this["areas"]; } - } - - [ConfigurationProperty("access")] - public AccessElement Access - { - get { return (AccessElement)this["access"]; } - } - - [ConfigurationCollection(typeof(SectionCollection), AddItemName = "tab")] - [ConfigurationProperty("", IsDefaultCollection = true)] - public TabCollection TabCollection - { - get { return (TabCollection)base[""]; } - set { base[""] = value; } - } - - IEnumerable ISection.Tabs - { - get { return TabCollection; } - } - - IEnumerable ISection.Areas - { - get { return Areas.AreaCollection.Cast().Select(x => x.Value); } - } - - IAccess ISection.AccessRights - { - get { return Access; } - } - } +using System.Collections.Generic; +using System.Configuration; +using System.Linq; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class SectionElement : ConfigurationElement, ISection + { + [ConfigurationProperty("alias", IsRequired = true)] + public string Alias + { + get { return (string) this["alias"]; } + } + + [ConfigurationProperty("areas", IsRequired = true)] + public AreasElement Areas + { + get { return (AreasElement)this["areas"]; } + } + + [ConfigurationProperty("access")] + public AccessElement Access + { + get { return (AccessElement)this["access"]; } + } + + [ConfigurationCollection(typeof(SectionCollection), AddItemName = "tab")] + [ConfigurationProperty("", IsDefaultCollection = true)] + public TabCollection TabCollection + { + get { return (TabCollection)base[""]; } + set { base[""] = value; } + } + + IEnumerable ISection.Tabs + { + get { return TabCollection; } + } + + IEnumerable ISection.Areas + { + get { return Areas.AreaCollection.Cast().Select(x => x.Value); } + } + + IAccess ISection.AccessRights + { + get { return Access; } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/TabCollection.cs b/src/Umbraco.Core/Configuration/Dashboard/TabCollection.cs index 6f0e018a40..42aa912114 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/TabCollection.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/TabCollection.cs @@ -1,37 +1,37 @@ -using System.Collections; -using System.Collections.Generic; -using System.Configuration; - -namespace Umbraco.Core.Configuration.Dashboard -{ - internal class TabCollection : ConfigurationElementCollection, IEnumerable - { - internal void Add(TabElement c) - { - BaseAdd(c); - } - - protected override ConfigurationElement CreateNewElement() - { - return new TabElement(); - } - - protected override object GetElementKey(ConfigurationElement element) - { - return ((TabElement)element).Caption; - } - - IEnumerator IEnumerable.GetEnumerator() - { - for (var i = 0; i < Count; i++) - { - yield return BaseGet(i) as IDashboardTab; - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } +using System.Collections; +using System.Collections.Generic; +using System.Configuration; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class TabCollection : ConfigurationElementCollection, IEnumerable + { + internal void Add(TabElement c) + { + BaseAdd(c); + } + + protected override ConfigurationElement CreateNewElement() + { + return new TabElement(); + } + + protected override object GetElementKey(ConfigurationElement element) + { + return ((TabElement)element).Caption; + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (var i = 0; i < Count; i++) + { + yield return BaseGet(i) as IDashboardTab; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Dashboard/TabElement.cs b/src/Umbraco.Core/Configuration/Dashboard/TabElement.cs index 2d83a7f5bb..abf3511db0 100644 --- a/src/Umbraco.Core/Configuration/Dashboard/TabElement.cs +++ b/src/Umbraco.Core/Configuration/Dashboard/TabElement.cs @@ -1,38 +1,38 @@ -using System.Collections.Generic; -using System.Configuration; - -namespace Umbraco.Core.Configuration.Dashboard -{ - internal class TabElement : ConfigurationElement, IDashboardTab - { - [ConfigurationProperty("caption", IsRequired = true)] - public string Caption - { - get { return (string)this["caption"]; } - } - - [ConfigurationProperty("access")] - public AccessElement Access - { - get { return (AccessElement)this["access"]; } - } - - [ConfigurationCollection(typeof(ControlCollection), AddItemName = "control")] - [ConfigurationProperty("", IsDefaultCollection = true)] - public ControlCollection ControlCollection - { - get { return (ControlCollection)base[""]; } - set { base[""] = value; } - } - - IEnumerable IDashboardTab.Controls - { - get { return ControlCollection; } - } - - IAccess IDashboardTab.AccessRights - { - get { return Access; } - } - } +using System.Collections.Generic; +using System.Configuration; + +namespace Umbraco.Core.Configuration.Dashboard +{ + internal class TabElement : ConfigurationElement, IDashboardTab + { + [ConfigurationProperty("caption", IsRequired = true)] + public string Caption + { + get { return (string)this["caption"]; } + } + + [ConfigurationProperty("access")] + public AccessElement Access + { + get { return (AccessElement)this["access"]; } + } + + [ConfigurationCollection(typeof(ControlCollection), AddItemName = "control")] + [ConfigurationProperty("", IsDefaultCollection = true)] + public ControlCollection ControlCollection + { + get { return (ControlCollection)base[""]; } + set { base[""] = value; } + } + + IEnumerable IDashboardTab.Controls + { + get { return ControlCollection; } + } + + IAccess IDashboardTab.AccessRights + { + get { return Access; } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/InnerTextConfigurationElement.cs b/src/Umbraco.Core/Configuration/InnerTextConfigurationElement.cs index 107f9ecca0..966bbc6a11 100644 --- a/src/Umbraco.Core/Configuration/InnerTextConfigurationElement.cs +++ b/src/Umbraco.Core/Configuration/InnerTextConfigurationElement.cs @@ -1,69 +1,69 @@ -using System; -using System.Xml; -using System.Xml.Linq; - -namespace Umbraco.Core.Configuration -{ - /// - /// A full config section is required for any full element and we have some elements that are defined like this: - /// {element}MyValue{/element} instead of as attribute values. - /// - /// - internal class InnerTextConfigurationElement : RawXmlConfigurationElement - { - public InnerTextConfigurationElement() - { - } - - public InnerTextConfigurationElement(XElement rawXml) : base(rawXml) - { - } - - protected override void DeserializeElement(XmlReader reader, bool serializeCollectionKey) - { - base.DeserializeElement(reader, serializeCollectionKey); - //now validate and set the raw value - if (RawXml.HasElements) - throw new InvalidOperationException("An InnerTextConfigurationElement cannot contain any child elements, only attributes and a value"); - RawValue = RawXml.Value.Trim(); - - //RawValue = reader.ReadElementContentAsString(); - } - - public virtual T Value - { - get - { - var converted = ObjectExtensions.TryConvertTo(RawValue); - if (converted.Success == false) - throw new InvalidCastException("Could not convert value " + RawValue + " to type " + typeof(T)); - return converted.Result; - } - } - - /// - /// Exposes the raw string value - /// - internal string RawValue { get; set; } - - /// - /// Implicit operator so we don't need to use the 'Value' property explicitly - /// - /// - /// - public static implicit operator T(InnerTextConfigurationElement m) - { - return m.Value; - } - - /// - /// Return the string value of Value - /// - /// - public override string ToString() - { - return string.Format("{0}", Value); - } - - } +using System; +using System.Xml; +using System.Xml.Linq; + +namespace Umbraco.Core.Configuration +{ + /// + /// A full config section is required for any full element and we have some elements that are defined like this: + /// {element}MyValue{/element} instead of as attribute values. + /// + /// + internal class InnerTextConfigurationElement : RawXmlConfigurationElement + { + public InnerTextConfigurationElement() + { + } + + public InnerTextConfigurationElement(XElement rawXml) : base(rawXml) + { + } + + protected override void DeserializeElement(XmlReader reader, bool serializeCollectionKey) + { + base.DeserializeElement(reader, serializeCollectionKey); + //now validate and set the raw value + if (RawXml.HasElements) + throw new InvalidOperationException("An InnerTextConfigurationElement cannot contain any child elements, only attributes and a value"); + RawValue = RawXml.Value.Trim(); + + //RawValue = reader.ReadElementContentAsString(); + } + + public virtual T Value + { + get + { + var converted = ObjectExtensions.TryConvertTo(RawValue); + if (converted.Success == false) + throw new InvalidCastException("Could not convert value " + RawValue + " to type " + typeof(T)); + return converted.Result; + } + } + + /// + /// Exposes the raw string value + /// + internal string RawValue { get; set; } + + /// + /// Implicit operator so we don't need to use the 'Value' property explicitly + /// + /// + /// + public static implicit operator T(InnerTextConfigurationElement m) + { + return m.Value; + } + + /// + /// Return the string value of Value + /// + /// + public override string ToString() + { + return string.Format("{0}", Value); + } + + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/OptionalCommaDelimitedConfigurationElement.cs b/src/Umbraco.Core/Configuration/OptionalCommaDelimitedConfigurationElement.cs index 18062a9ed9..59ad883e03 100644 --- a/src/Umbraco.Core/Configuration/OptionalCommaDelimitedConfigurationElement.cs +++ b/src/Umbraco.Core/Configuration/OptionalCommaDelimitedConfigurationElement.cs @@ -1,43 +1,43 @@ -using System.Configuration; -using Umbraco.Core.Configuration.UmbracoSettings; - -namespace Umbraco.Core.Configuration -{ - /// - /// Used for specifying default values for comma delimited config - /// - internal class OptionalCommaDelimitedConfigurationElement : CommaDelimitedConfigurationElement - { - private readonly CommaDelimitedConfigurationElement _wrapped; - private readonly string[] _defaultValue; - - public OptionalCommaDelimitedConfigurationElement() - { - } - - public OptionalCommaDelimitedConfigurationElement(CommaDelimitedConfigurationElement wrapped, string[] defaultValue) - { - _wrapped = wrapped; - _defaultValue = defaultValue; - } - - public override CommaDelimitedStringCollection Value - { - get - { - if (_wrapped == null) - { - return base.Value; - } - - if (string.IsNullOrEmpty(_wrapped.RawValue)) - { - var val = new CommaDelimitedStringCollection(); - val.AddRange(_defaultValue); - return val; - } - return _wrapped.Value; - } - } - } +using System.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Core.Configuration +{ + /// + /// Used for specifying default values for comma delimited config + /// + internal class OptionalCommaDelimitedConfigurationElement : CommaDelimitedConfigurationElement + { + private readonly CommaDelimitedConfigurationElement _wrapped; + private readonly string[] _defaultValue; + + public OptionalCommaDelimitedConfigurationElement() + { + } + + public OptionalCommaDelimitedConfigurationElement(CommaDelimitedConfigurationElement wrapped, string[] defaultValue) + { + _wrapped = wrapped; + _defaultValue = defaultValue; + } + + public override CommaDelimitedStringCollection Value + { + get + { + if (_wrapped == null) + { + return base.Value; + } + + if (string.IsNullOrEmpty(_wrapped.RawValue)) + { + var val = new CommaDelimitedStringCollection(); + val.AddRange(_defaultValue); + return val; + } + return _wrapped.Value; + } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/OptionalInnerTextConfigurationElement.cs b/src/Umbraco.Core/Configuration/OptionalInnerTextConfigurationElement.cs index a2daf059b4..832a74fec2 100644 --- a/src/Umbraco.Core/Configuration/OptionalInnerTextConfigurationElement.cs +++ b/src/Umbraco.Core/Configuration/OptionalInnerTextConfigurationElement.cs @@ -1,23 +1,23 @@ -namespace Umbraco.Core.Configuration -{ - /// - /// This is used to supply optional/default values when using InnerTextConfigurationElement - /// - /// - internal class OptionalInnerTextConfigurationElement : InnerTextConfigurationElement - { - private readonly InnerTextConfigurationElement _wrapped; - private readonly T _defaultValue; - - public OptionalInnerTextConfigurationElement(InnerTextConfigurationElement wrapped, T defaultValue) - { - _wrapped = wrapped; - _defaultValue = defaultValue; - } - - public override T Value - { - get { return string.IsNullOrEmpty(_wrapped.RawValue) ? _defaultValue : _wrapped.Value; } - } - } +namespace Umbraco.Core.Configuration +{ + /// + /// This is used to supply optional/default values when using InnerTextConfigurationElement + /// + /// + internal class OptionalInnerTextConfigurationElement : InnerTextConfigurationElement + { + private readonly InnerTextConfigurationElement _wrapped; + private readonly T _defaultValue; + + public OptionalInnerTextConfigurationElement(InnerTextConfigurationElement wrapped, T defaultValue) + { + _wrapped = wrapped; + _defaultValue = defaultValue; + } + + public override T Value + { + get { return string.IsNullOrEmpty(_wrapped.RawValue) ? _defaultValue : _wrapped.Value; } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/RawXmlConfigurationElement.cs b/src/Umbraco.Core/Configuration/RawXmlConfigurationElement.cs index aec3af9481..f2d2d8a52f 100644 --- a/src/Umbraco.Core/Configuration/RawXmlConfigurationElement.cs +++ b/src/Umbraco.Core/Configuration/RawXmlConfigurationElement.cs @@ -1,30 +1,30 @@ -using System.Configuration; -using System.Xml; -using System.Xml.Linq; - -namespace Umbraco.Core.Configuration -{ - /// - /// A configuration section that simply exposes the entire raw xml of the section itself which inheritors can use - /// to do with as they please. - /// - internal abstract class RawXmlConfigurationElement : ConfigurationElement - { - protected RawXmlConfigurationElement() - { - - } - - protected RawXmlConfigurationElement(XElement rawXml) - { - RawXml = rawXml; - } - - protected override void DeserializeElement(XmlReader reader, bool serializeCollectionKey) - { - RawXml = (XElement)XNode.ReadFrom(reader); - } - - protected XElement RawXml { get; private set; } - } +using System.Configuration; +using System.Xml; +using System.Xml.Linq; + +namespace Umbraco.Core.Configuration +{ + /// + /// A configuration section that simply exposes the entire raw xml of the section itself which inheritors can use + /// to do with as they please. + /// + internal abstract class RawXmlConfigurationElement : ConfigurationElement + { + protected RawXmlConfigurationElement() + { + + } + + protected RawXmlConfigurationElement(XElement rawXml) + { + RawXml = rawXml; + } + + protected override void DeserializeElement(XmlReader reader, bool serializeCollectionKey) + { + RawXml = (XElement)XNode.ReadFrom(reader); + } + + protected XElement RawXml { get; private set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoConfig.cs b/src/Umbraco.Core/Configuration/UmbracoConfig.cs index 27f8b9b6a0..27fb572300 100644 --- a/src/Umbraco.Core/Configuration/UmbracoConfig.cs +++ b/src/Umbraco.Core/Configuration/UmbracoConfig.cs @@ -1,127 +1,127 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Configuration; -using System.Linq; -using System.Threading; -using Umbraco.Core.Configuration.BaseRest; -using Umbraco.Core.Configuration.Dashboard; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Logging; - -namespace Umbraco.Core.Configuration -{ - /// - /// The gateway to all umbraco configuration - /// - public class UmbracoConfig - { - #region Singleton - - private static readonly Lazy Lazy = new Lazy(() => new UmbracoConfig()); - - public static UmbracoConfig For - { - get { return Lazy.Value; } - } - - #endregion - - /// - /// Default constructor - /// - private UmbracoConfig() - { - if (_umbracoSettings == null) - { - var umbracoSettings = ConfigurationManager.GetSection("umbracoConfiguration/settings") as IUmbracoSettingsSection; - if (umbracoSettings == null) - { - LogHelper.Warn("Could not load the " + typeof(IUmbracoSettingsSection) + " from config file!"); - } - SetUmbracoSettings(umbracoSettings); - } - - if (_baseRestExtensions == null) - { - var baseRestExtensions = ConfigurationManager.GetSection("umbracoConfiguration/BaseRestExtensions") as IBaseRestSection; - if (baseRestExtensions == null) - { - LogHelper.Warn("Could not load the " + typeof(IBaseRestSection) + " from config file!"); - } - SetBaseRestExtensions(baseRestExtensions); - } - - if (_dashboardSection == null) - { - var dashboardConfig = ConfigurationManager.GetSection("umbracoConfiguration/dashBoard") as IDashboardSection; - if (dashboardConfig == null) - { - LogHelper.Warn("Could not load the " + typeof(IDashboardSection) + " from config file!"); - } - SetDashboardSettings(dashboardConfig); - } - } - - /// - /// Constructor - can be used for testing - /// - /// - /// - /// - public UmbracoConfig(IUmbracoSettingsSection umbracoSettings, IBaseRestSection baseRestSettings, IDashboardSection dashboardSettings) - { - SetUmbracoSettings(umbracoSettings); - SetBaseRestExtensions(baseRestSettings); - SetDashboardSettings(dashboardSettings); - } - - private IDashboardSection _dashboardSection; - private IUmbracoSettingsSection _umbracoSettings; - private IBaseRestSection _baseRestExtensions; - - /// - /// Gets the IDashboardSection - /// - public IDashboardSection DashboardSettings() - { - return _dashboardSection; - } - - //ONLY for unit testing - internal void SetDashboardSettings(IDashboardSection value) - { - _dashboardSection = value; - } - - //ONLY for unit testing - internal void SetUmbracoSettings(IUmbracoSettingsSection value) - { - _umbracoSettings = value; - } - - /// - /// Gets the IUmbracoSettings - /// - public IUmbracoSettingsSection UmbracoSettings() - { - return _umbracoSettings; - } - - //ONLY for unit testing - public void SetBaseRestExtensions(IBaseRestSection value) - { - _baseRestExtensions = value; - } - - /// - /// Gets the IBaseRestSection - /// - public IBaseRestSection BaseRestExtensions() - { - return _baseRestExtensions; - } - - //TODO: Add other configurations here ! - } +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Configuration; +using System.Linq; +using System.Threading; +using Umbraco.Core.Configuration.BaseRest; +using Umbraco.Core.Configuration.Dashboard; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Logging; + +namespace Umbraco.Core.Configuration +{ + /// + /// The gateway to all umbraco configuration + /// + public class UmbracoConfig + { + #region Singleton + + private static readonly Lazy Lazy = new Lazy(() => new UmbracoConfig()); + + public static UmbracoConfig For + { + get { return Lazy.Value; } + } + + #endregion + + /// + /// Default constructor + /// + private UmbracoConfig() + { + if (_umbracoSettings == null) + { + var umbracoSettings = ConfigurationManager.GetSection("umbracoConfiguration/settings") as IUmbracoSettingsSection; + if (umbracoSettings == null) + { + LogHelper.Warn("Could not load the " + typeof(IUmbracoSettingsSection) + " from config file!"); + } + SetUmbracoSettings(umbracoSettings); + } + + if (_baseRestExtensions == null) + { + var baseRestExtensions = ConfigurationManager.GetSection("umbracoConfiguration/BaseRestExtensions") as IBaseRestSection; + if (baseRestExtensions == null) + { + LogHelper.Warn("Could not load the " + typeof(IBaseRestSection) + " from config file!"); + } + SetBaseRestExtensions(baseRestExtensions); + } + + if (_dashboardSection == null) + { + var dashboardConfig = ConfigurationManager.GetSection("umbracoConfiguration/dashBoard") as IDashboardSection; + if (dashboardConfig == null) + { + LogHelper.Warn("Could not load the " + typeof(IDashboardSection) + " from config file!"); + } + SetDashboardSettings(dashboardConfig); + } + } + + /// + /// Constructor - can be used for testing + /// + /// + /// + /// + public UmbracoConfig(IUmbracoSettingsSection umbracoSettings, IBaseRestSection baseRestSettings, IDashboardSection dashboardSettings) + { + SetUmbracoSettings(umbracoSettings); + SetBaseRestExtensions(baseRestSettings); + SetDashboardSettings(dashboardSettings); + } + + private IDashboardSection _dashboardSection; + private IUmbracoSettingsSection _umbracoSettings; + private IBaseRestSection _baseRestExtensions; + + /// + /// Gets the IDashboardSection + /// + public IDashboardSection DashboardSettings() + { + return _dashboardSection; + } + + //ONLY for unit testing + internal void SetDashboardSettings(IDashboardSection value) + { + _dashboardSection = value; + } + + //ONLY for unit testing + internal void SetUmbracoSettings(IUmbracoSettingsSection value) + { + _umbracoSettings = value; + } + + /// + /// Gets the IUmbracoSettings + /// + public IUmbracoSettingsSection UmbracoSettings() + { + return _umbracoSettings; + } + + //ONLY for unit testing + public void SetBaseRestExtensions(IBaseRestSection value) + { + _baseRestExtensions = value; + } + + /// + /// Gets the IBaseRestSection + /// + public IBaseRestSection BaseRestExtensions() + { + return _baseRestExtensions; + } + + //TODO: Add other configurations here ! + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IChar.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IChar.cs index 7b5775cc48..4ec15603c2 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IChar.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IChar.cs @@ -1,8 +1,8 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IChar - { - string Char { get; } - string Replacement { get; } - } +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IChar + { + string Char { get; } + string Replacement { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentErrorPage.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentErrorPage.cs index 3253d77bed..be342b1fb6 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentErrorPage.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentErrorPage.cs @@ -1,8 +1,8 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IContentErrorPage - { - int ContentId { get; } - string Culture { get; set; } - } +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IContentErrorPage + { + int ContentId { get; } + string Culture { get; set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs index 0ae68b751c..93e3260b44 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs @@ -1,63 +1,63 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IContentSection : IUmbracoConfigurationSection - { - string NotificationEmailAddress { get; } - - bool DisableHtmlEmail { get; } - - IEnumerable ImageFileTypes { get; } - - IEnumerable ImageTagAllowedAttributes { get; } - - IEnumerable ImageAutoFillProperties { get; } - - string ScriptFolderPath { get; } - - IEnumerable ScriptFileTypes { get; } - - bool ScriptEditorDisable { get; } - - bool ResolveUrlsFromTextString { get; } - - bool UploadAllowDirectories { get; } - - IEnumerable Error404Collection { get; } - - bool EnsureUniqueNaming { get; } - - bool TidyEditorContent { get; } - - string TidyCharEncoding { get; } - - bool XmlCacheEnabled { get; } - - bool ContinouslyUpdateXmlDiskCache { get; } - - bool XmlContentCheckForDiskChanges { get; } - - bool EnableSplashWhileLoading { get; } - - string PropertyContextHelpOption { get; } - - bool UseLegacyXmlSchema { get; } - - bool ForceSafeAliases { get; } - - string PreviewBadge { get; } - - int UmbracoLibraryCacheDuration { get; } - - MacroErrorBehaviour MacroErrorBehaviour { get; } - - IEnumerable DisallowedUploadFiles { get; } - - bool CloneXmlContent { get; } - - bool GlobalPreviewStorageEnabled { get; } - - string DefaultDocumentTypeProperty { get; } - } +using System.Collections.Generic; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IContentSection : IUmbracoConfigurationSection + { + string NotificationEmailAddress { get; } + + bool DisableHtmlEmail { get; } + + IEnumerable ImageFileTypes { get; } + + IEnumerable ImageTagAllowedAttributes { get; } + + IEnumerable ImageAutoFillProperties { get; } + + string ScriptFolderPath { get; } + + IEnumerable ScriptFileTypes { get; } + + bool ScriptEditorDisable { get; } + + bool ResolveUrlsFromTextString { get; } + + bool UploadAllowDirectories { get; } + + IEnumerable Error404Collection { get; } + + bool EnsureUniqueNaming { get; } + + bool TidyEditorContent { get; } + + string TidyCharEncoding { get; } + + bool XmlCacheEnabled { get; } + + bool ContinouslyUpdateXmlDiskCache { get; } + + bool XmlContentCheckForDiskChanges { get; } + + bool EnableSplashWhileLoading { get; } + + string PropertyContextHelpOption { get; } + + bool UseLegacyXmlSchema { get; } + + bool ForceSafeAliases { get; } + + string PreviewBadge { get; } + + int UmbracoLibraryCacheDuration { get; } + + MacroErrorBehaviour MacroErrorBehaviour { get; } + + IEnumerable DisallowedUploadFiles { get; } + + bool CloneXmlContent { get; } + + bool GlobalPreviewStorageEnabled { get; } + + string DefaultDocumentTypeProperty { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IDeveloperSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IDeveloperSection.cs index df6f8c0ed7..a1f1bffd3e 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IDeveloperSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IDeveloperSection.cs @@ -1,9 +1,9 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IDeveloperSection : IUmbracoConfigurationSection - { - IEnumerable AppCodeFileExtensions { get; } - } +using System.Collections.Generic; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IDeveloperSection : IUmbracoConfigurationSection + { + IEnumerable AppCodeFileExtensions { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IDistributedCallSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IDistributedCallSection.cs index f3c0d5247a..5f8e51d60f 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IDistributedCallSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IDistributedCallSection.cs @@ -1,13 +1,13 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IDistributedCallSection : IUmbracoConfigurationSection - { - bool Enabled { get; } - - int UserId { get; } - - IEnumerable Servers { get; } - } +using System.Collections.Generic; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IDistributedCallSection : IUmbracoConfigurationSection + { + bool Enabled { get; } + + int UserId { get; } + + IEnumerable Servers { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IFileExtension.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IFileExtension.cs index 91629f50ce..2d9ee19fcd 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IFileExtension.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IFileExtension.cs @@ -1,7 +1,7 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IFileExtension - { - string Extension { get; } - } +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IFileExtension + { + string Extension { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IHelpSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IHelpSection.cs index e8ee7195ee..61be2dfaf2 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IHelpSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IHelpSection.cs @@ -1,11 +1,11 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IHelpSection : IUmbracoConfigurationSection - { - string DefaultUrl { get; } - - IEnumerable Links { get; } - } +using System.Collections.Generic; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IHelpSection : IUmbracoConfigurationSection + { + string DefaultUrl { get; } + + IEnumerable Links { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IImagingAutoFillUploadField.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IImagingAutoFillUploadField.cs index 5dbeee02d0..6f42e14668 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IImagingAutoFillUploadField.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IImagingAutoFillUploadField.cs @@ -1,18 +1,18 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IImagingAutoFillUploadField - { - /// - /// Allow setting internally so we can create a default - /// - string Alias { get; } - - string WidthFieldAlias { get; } - - string HeightFieldAlias { get; } - - string LengthFieldAlias { get; } - - string ExtensionFieldAlias { get; } - } +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IImagingAutoFillUploadField + { + /// + /// Allow setting internally so we can create a default + /// + string Alias { get; } + + string WidthFieldAlias { get; } + + string HeightFieldAlias { get; } + + string LengthFieldAlias { get; } + + string ExtensionFieldAlias { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ILink.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ILink.cs index 2085d29325..d2afec55f3 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ILink.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ILink.cs @@ -1,15 +1,15 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface ILink - { - string Application { get; } - - string ApplicationUrl { get; } - - string Language { get; } - - string UserType { get; } - - string HelpUrl { get; } - } +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface ILink + { + string Application { get; } + + string ApplicationUrl { get; } + + string Language { get; } + + string UserType { get; } + + string HelpUrl { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ILogType.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ILogType.cs index 68df770be0..559fb1fb13 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ILogType.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ILogType.cs @@ -1,7 +1,7 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface ILogType - { - string LogTypeAlias { get; } - } +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface ILogType + { + string LogTypeAlias { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ILoggingSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ILoggingSection.cs index df75a82f64..8a9a6ad29a 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ILoggingSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ILoggingSection.cs @@ -1,27 +1,27 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface ILoggingSection : IUmbracoConfigurationSection - { - bool AutoCleanLogs { get; } - - bool EnableLogging { get; } - - bool EnableAsyncLogging { get; } - - int CleaningMiliseconds { get; } - - int MaxLogAge { get; } - - IEnumerable DisabledLogTypes { get; } - - string ExternalLoggerAssembly { get; } - - string ExternalLoggerType { get; } - - bool ExternalLoggerEnableAuditTrail { get; } - - bool ExternalLoggerIsConfigured { get; } - } +using System.Collections.Generic; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface ILoggingSection : IUmbracoConfigurationSection + { + bool AutoCleanLogs { get; } + + bool EnableLogging { get; } + + bool EnableAsyncLogging { get; } + + int CleaningMiliseconds { get; } + + int MaxLogAge { get; } + + IEnumerable DisabledLogTypes { get; } + + string ExternalLoggerAssembly { get; } + + string ExternalLoggerType { get; } + + bool ExternalLoggerEnableAuditTrail { get; } + + bool ExternalLoggerIsConfigured { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/INotDynamicXmlDocument.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/INotDynamicXmlDocument.cs index 92c6ea990c..3ab1a9a67b 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/INotDynamicXmlDocument.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/INotDynamicXmlDocument.cs @@ -1,7 +1,7 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface INotDynamicXmlDocument - { - string Element { get; } - } +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface INotDynamicXmlDocument + { + string Element { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IProvidersSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IProvidersSection.cs index 6234cc6f2c..8a61bfffb3 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IProvidersSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IProvidersSection.cs @@ -1,9 +1,9 @@ -using System.Configuration; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IProvidersSection : IUmbracoConfigurationSection - { - string DefaultBackOfficeUserProvider { get; } - } +using System.Configuration; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IProvidersSection : IUmbracoConfigurationSection + { + string DefaultBackOfficeUserProvider { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IRazorStaticMapping.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IRazorStaticMapping.cs index 471afce319..a330fbcfac 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IRazorStaticMapping.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IRazorStaticMapping.cs @@ -1,12 +1,12 @@ -using System; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IRazorStaticMapping - { - Guid DataTypeGuid { get; } - string NodeTypeAlias { get; } - string PropertyTypeAlias { get; } - string MappingName { get; } - } +using System; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IRazorStaticMapping + { + Guid DataTypeGuid { get; } + string NodeTypeAlias { get; } + string PropertyTypeAlias { get; } + string MappingName { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IRepositoriesSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IRepositoriesSection.cs index 746cc030e5..063acbe1cf 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IRepositoriesSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IRepositoriesSection.cs @@ -1,10 +1,10 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - - public interface IRepositoriesSection : IUmbracoConfigurationSection - { - IEnumerable Repositories { get; } - } +using System.Collections.Generic; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + + public interface IRepositoriesSection : IUmbracoConfigurationSection + { + IEnumerable Repositories { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IRepository.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IRepository.cs index bb1757748c..052c23edd5 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IRepository.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IRepository.cs @@ -1,13 +1,13 @@ -using System; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IRepository - { - string Name { get; } - Guid Id { get; } - string RepositoryUrl { get; } - string WebServiceUrl { get; } - bool HasCustomWebServiceUrl { get; } - } +using System; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IRepository + { + string Name { get; } + Guid Id { get; } + string RepositoryUrl { get; } + string WebServiceUrl { get; } + bool HasCustomWebServiceUrl { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IRequestHandlerSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IRequestHandlerSection.cs index db11cfe883..43e6da24ac 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IRequestHandlerSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IRequestHandlerSection.cs @@ -1,15 +1,15 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IRequestHandlerSection : IUmbracoConfigurationSection - { - bool UseDomainPrefixes { get; } - - bool AddTrailingSlash { get; } - - bool RemoveDoubleDashes { get; } - - IEnumerable CharCollection { get; } - } +using System.Collections.Generic; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IRequestHandlerSection : IUmbracoConfigurationSection + { + bool UseDomainPrefixes { get; } + + bool AddTrailingSlash { get; } + + bool RemoveDoubleDashes { get; } + + IEnumerable CharCollection { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IScheduledTask.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IScheduledTask.cs index 4be62fbd17..46d4256170 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IScheduledTask.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IScheduledTask.cs @@ -1,13 +1,13 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IScheduledTask - { - string Alias { get; } - - bool Log { get; } - - int Interval { get; } - - string Url { get; } - } +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IScheduledTask + { + string Alias { get; } + + bool Log { get; } + + int Interval { get; } + + string Url { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IScheduledTasksSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IScheduledTasksSection.cs index 334b97bfba..9d01549a5c 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IScheduledTasksSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IScheduledTasksSection.cs @@ -1,9 +1,9 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IScheduledTasksSection : IUmbracoConfigurationSection - { - IEnumerable Tasks { get; } - } +using System.Collections.Generic; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IScheduledTasksSection : IUmbracoConfigurationSection + { + IEnumerable Tasks { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IScriptingSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IScriptingSection.cs index 37e93d55e8..c28dce98b8 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IScriptingSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IScriptingSection.cs @@ -1,11 +1,11 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IScriptingSection : IUmbracoConfigurationSection - { - IEnumerable NotDynamicXmlDocumentElements { get; } - - IEnumerable DataTypeModelStaticMappings { get; } - } +using System.Collections.Generic; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IScriptingSection : IUmbracoConfigurationSection + { + IEnumerable NotDynamicXmlDocumentElements { get; } + + IEnumerable DataTypeModelStaticMappings { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySection.cs index 37ff79ff07..c3a1df301d 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySection.cs @@ -1,13 +1,13 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface ISecuritySection : IUmbracoConfigurationSection - { - bool KeepUserLoggedIn { get; } - - bool HideDisabledUsersInBackoffice { get; } - - string AuthCookieName { get; } - - string AuthCookieDomain { get; } - } +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface ISecuritySection : IUmbracoConfigurationSection + { + bool KeepUserLoggedIn { get; } + + bool HideDisabledUsersInBackoffice { get; } + + string AuthCookieName { get; } + + string AuthCookieDomain { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IServer.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IServer.cs index d8fd584bff..5ad0715302 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IServer.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IServer.cs @@ -1,9 +1,9 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IServer - { - string ForcePortnumber { get; } - string ForceProtocol { get; } - string ServerAddress { get; } - } +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IServer + { + string ForcePortnumber { get; } + string ForceProtocol { get; } + string ServerAddress { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ITemplatesSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ITemplatesSection.cs index 8db3a411ee..f6d104d291 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ITemplatesSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ITemplatesSection.cs @@ -1,13 +1,13 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface ITemplatesSection : IUmbracoConfigurationSection - { - bool UseAspNetMasterPages { get; } - - bool EnableSkinSupport { get; } - - RenderingEngine DefaultRenderingEngine { get; } - - bool EnableTemplateFolders { get; } - } +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface ITemplatesSection : IUmbracoConfigurationSection + { + bool UseAspNetMasterPages { get; } + + bool EnableSkinSupport { get; } + + RenderingEngine DefaultRenderingEngine { get; } + + bool EnableTemplateFolders { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs index 5189d3002d..899de7d1f9 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs @@ -1,33 +1,33 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IUmbracoSettingsSection : IUmbracoConfigurationSection - { - IContentSection Content { get; } - - ISecuritySection Security { get; } - - IRequestHandlerSection RequestHandler { get; } - - ITemplatesSection Templates { get; } - - IDeveloperSection Developer { get; } - - IViewStateMoverModuleSection ViewStateMoverModule { get; } - - ILoggingSection Logging { get; } - - IScheduledTasksSection ScheduledTasks { get; } - - IDistributedCallSection DistributedCall { get; } - - IRepositoriesSection PackageRepositories { get; } - - IProvidersSection Providers { get; } - - IHelpSection Help { get; } - - IWebRoutingSection WebRouting { get; } - - IScriptingSection Scripting { get; } - } +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IUmbracoSettingsSection : IUmbracoConfigurationSection + { + IContentSection Content { get; } + + ISecuritySection Security { get; } + + IRequestHandlerSection RequestHandler { get; } + + ITemplatesSection Templates { get; } + + IDeveloperSection Developer { get; } + + IViewStateMoverModuleSection ViewStateMoverModule { get; } + + ILoggingSection Logging { get; } + + IScheduledTasksSection ScheduledTasks { get; } + + IDistributedCallSection DistributedCall { get; } + + IRepositoriesSection PackageRepositories { get; } + + IProvidersSection Providers { get; } + + IHelpSection Help { get; } + + IWebRoutingSection WebRouting { get; } + + IScriptingSection Scripting { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IViewStateMoverModuleSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IViewStateMoverModuleSection.cs index 953e18ef1e..150827dccb 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IViewStateMoverModuleSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IViewStateMoverModuleSection.cs @@ -1,7 +1,7 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IViewStateMoverModuleSection : IUmbracoConfigurationSection - { - bool Enable { get; } - } +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IViewStateMoverModuleSection : IUmbracoConfigurationSection + { + bool Enable { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs index 73177ed2af..03fad06d82 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs @@ -1,12 +1,12 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IWebRoutingSection : IUmbracoConfigurationSection - { - bool TrySkipIisCustomErrors { get; } - - bool InternalRedirectPreservesTemplate { get; } - - string UrlProviderMode { get; } - } - +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IWebRoutingSection : IUmbracoConfigurationSection + { + bool TrySkipIisCustomErrors { get; } + + bool InternalRedirectPreservesTemplate { get; } + + string UrlProviderMode { get; } + } + } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ImagingAutoFillPropertiesCollection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ImagingAutoFillPropertiesCollection.cs index 981172349d..28cca97c8e 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ImagingAutoFillPropertiesCollection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ImagingAutoFillPropertiesCollection.cs @@ -1,37 +1,37 @@ -using System.Collections.Generic; -using System.Configuration; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - internal class ImagingAutoFillPropertiesCollection : ConfigurationElementCollection, IEnumerable - { - - protected override ConfigurationElement CreateNewElement() - { - return new ImagingAutoFillUploadFieldElement(); - } - - protected override object GetElementKey(ConfigurationElement element) - { - return ((ImagingAutoFillUploadFieldElement)element).Alias; - } - - internal void Add(ImagingAutoFillUploadFieldElement item) - { - BaseAdd(item); - } - - IEnumerator IEnumerable.GetEnumerator() - { - for (var i = 0; i < Count; i++) - { - yield return BaseGet(i) as IImagingAutoFillUploadField; - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } +using System.Collections.Generic; +using System.Configuration; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + internal class ImagingAutoFillPropertiesCollection : ConfigurationElementCollection, IEnumerable + { + + protected override ConfigurationElement CreateNewElement() + { + return new ImagingAutoFillUploadFieldElement(); + } + + protected override object GetElementKey(ConfigurationElement element) + { + return ((ImagingAutoFillUploadFieldElement)element).Alias; + } + + internal void Add(ImagingAutoFillUploadFieldElement item) + { + BaseAdd(item); + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (var i = 0; i < Count; i++) + { + yield return BaseGet(i) as IImagingAutoFillUploadField; + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ImagingAutoFillUploadFieldElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ImagingAutoFillUploadFieldElement.cs index e23aae8b8e..0dfc4afc00 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ImagingAutoFillUploadFieldElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ImagingAutoFillUploadFieldElement.cs @@ -1,91 +1,91 @@ -using System.Configuration; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - internal class ImagingAutoFillUploadFieldElement : ConfigurationElement, IImagingAutoFillUploadField - { - /// - /// Allow setting internally so we can create a default - /// - [ConfigurationProperty("alias", IsKey = true, IsRequired = true)] - public string Alias - { - get { return (string)this["alias"]; } - set { this["alias"] = value; } - } - - [ConfigurationProperty("widthFieldAlias")] - internal InnerTextConfigurationElement WidthFieldAlias - { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["widthFieldAlias"], - //set the default - "umbracoWidth"); - } - } - - [ConfigurationProperty("heightFieldAlias")] - internal InnerTextConfigurationElement HeightFieldAlias - { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["heightFieldAlias"], - //set the default - "umbracoHeight"); - } - } - - [ConfigurationProperty("lengthFieldAlias")] - internal InnerTextConfigurationElement LengthFieldAlias - { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["lengthFieldAlias"], - //set the default - "umbracoBytes"); - } - } - - [ConfigurationProperty("extensionFieldAlias")] - internal InnerTextConfigurationElement ExtensionFieldAlias - { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["extensionFieldAlias"], - //set the default - "umbracoExtension"); - } - } - - string IImagingAutoFillUploadField.Alias - { - get { return Alias; } - - } - - string IImagingAutoFillUploadField.WidthFieldAlias - { - get { return WidthFieldAlias; } - } - - string IImagingAutoFillUploadField.HeightFieldAlias - { - get { return HeightFieldAlias; } - } - - string IImagingAutoFillUploadField.LengthFieldAlias - { - get { return LengthFieldAlias; } - } - - string IImagingAutoFillUploadField.ExtensionFieldAlias - { - get { return ExtensionFieldAlias; } - } - } +using System.Configuration; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + internal class ImagingAutoFillUploadFieldElement : ConfigurationElement, IImagingAutoFillUploadField + { + /// + /// Allow setting internally so we can create a default + /// + [ConfigurationProperty("alias", IsKey = true, IsRequired = true)] + public string Alias + { + get { return (string)this["alias"]; } + set { this["alias"] = value; } + } + + [ConfigurationProperty("widthFieldAlias")] + internal InnerTextConfigurationElement WidthFieldAlias + { + get + { + return new OptionalInnerTextConfigurationElement( + (InnerTextConfigurationElement)this["widthFieldAlias"], + //set the default + "umbracoWidth"); + } + } + + [ConfigurationProperty("heightFieldAlias")] + internal InnerTextConfigurationElement HeightFieldAlias + { + get + { + return new OptionalInnerTextConfigurationElement( + (InnerTextConfigurationElement)this["heightFieldAlias"], + //set the default + "umbracoHeight"); + } + } + + [ConfigurationProperty("lengthFieldAlias")] + internal InnerTextConfigurationElement LengthFieldAlias + { + get + { + return new OptionalInnerTextConfigurationElement( + (InnerTextConfigurationElement)this["lengthFieldAlias"], + //set the default + "umbracoBytes"); + } + } + + [ConfigurationProperty("extensionFieldAlias")] + internal InnerTextConfigurationElement ExtensionFieldAlias + { + get + { + return new OptionalInnerTextConfigurationElement( + (InnerTextConfigurationElement)this["extensionFieldAlias"], + //set the default + "umbracoExtension"); + } + } + + string IImagingAutoFillUploadField.Alias + { + get { return Alias; } + + } + + string IImagingAutoFillUploadField.WidthFieldAlias + { + get { return WidthFieldAlias; } + } + + string IImagingAutoFillUploadField.HeightFieldAlias + { + get { return HeightFieldAlias; } + } + + string IImagingAutoFillUploadField.LengthFieldAlias + { + get { return LengthFieldAlias; } + } + + string IImagingAutoFillUploadField.ExtensionFieldAlias + { + get { return ExtensionFieldAlias; } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Constants-Examine.cs b/src/Umbraco.Core/Constants-Examine.cs index f040634506..4ff6115749 100644 --- a/src/Umbraco.Core/Constants-Examine.cs +++ b/src/Umbraco.Core/Constants-Examine.cs @@ -1,24 +1,24 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Umbraco.Core -{ - public static partial class Constants - { - public static class Examine - { - /// - /// The alias of the internal member searcher - /// - public const string InternalMemberSearcher = "InternalMemberSearcher"; - - /// - /// The alias of the internal content searcher - /// - public const string InternalSearcher = "InternalSearcher"; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Core +{ + public static partial class Constants + { + public static class Examine + { + /// + /// The alias of the internal member searcher + /// + public const string InternalMemberSearcher = "InternalMemberSearcher"; + + /// + /// The alias of the internal content searcher + /// + public const string InternalSearcher = "InternalSearcher"; + } + } +} diff --git a/src/Umbraco.Core/IDisposeOnRequestEnd.cs b/src/Umbraco.Core/IDisposeOnRequestEnd.cs index cf1ec3a177..111b97b842 100644 --- a/src/Umbraco.Core/IDisposeOnRequestEnd.cs +++ b/src/Umbraco.Core/IDisposeOnRequestEnd.cs @@ -1,14 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Umbraco.Core -{ - /// - /// Any class implementing this interface that is added to the httpcontext.items keys or values will be disposed of at the end of the request. - /// - public interface IDisposeOnRequestEnd : IDisposable - { - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Umbraco.Core +{ + /// + /// Any class implementing this interface that is added to the httpcontext.items keys or values will be disposed of at the end of the request. + /// + public interface IDisposeOnRequestEnd : IDisposable + { + } +} diff --git a/src/Umbraco.Core/Manifest/ParameterEditorConverter.cs b/src/Umbraco.Core/Manifest/ParameterEditorConverter.cs index 6d60dd3ab8..5f25f9ad81 100644 --- a/src/Umbraco.Core/Manifest/ParameterEditorConverter.cs +++ b/src/Umbraco.Core/Manifest/ParameterEditorConverter.cs @@ -1,31 +1,31 @@ -using System; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Serialization; - -namespace Umbraco.Core.Manifest -{ - /// - /// Used to convert a parameter editor manifest to a property editor object - /// - internal class ParameterEditorConverter : JsonCreationConverter - { - protected override ParameterEditor Create(Type objectType, JObject jObject) - { - return new ParameterEditor(); - } - - protected override void Deserialize(JObject jObject, ParameterEditor target, JsonSerializer serializer) - { - //since it's a manifest editor, we need to create it's instance. - //we need to specify the view value for the editor here otherwise we'll get an exception. - target.ManifestDefinedParameterValueEditor = new ParameterValueEditor - { - View = jObject["view"].ToString() - }; - - base.Deserialize(jObject, target, serializer); - } - } +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Serialization; + +namespace Umbraco.Core.Manifest +{ + /// + /// Used to convert a parameter editor manifest to a property editor object + /// + internal class ParameterEditorConverter : JsonCreationConverter + { + protected override ParameterEditor Create(Type objectType, JObject jObject) + { + return new ParameterEditor(); + } + + protected override void Deserialize(JObject jObject, ParameterEditor target, JsonSerializer serializer) + { + //since it's a manifest editor, we need to create it's instance. + //we need to specify the view value for the editor here otherwise we'll get an exception. + target.ManifestDefinedParameterValueEditor = new ParameterValueEditor + { + View = jObject["view"].ToString() + }; + + base.Deserialize(jObject, target, serializer); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Media/ImageHelper.cs b/src/Umbraco.Core/Media/ImageHelper.cs index 02454fc648..d419f6ffdc 100644 --- a/src/Umbraco.Core/Media/ImageHelper.cs +++ b/src/Umbraco.Core/Media/ImageHelper.cs @@ -1,144 +1,144 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Drawing.Imaging; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Umbraco.Core.Configuration; -using Umbraco.Core.IO; - - -namespace Umbraco.Core.Media -{ - /// - /// A helper class used for imaging - /// - internal static class ImageHelper - { - public static string GetMimeType(this Image image) - { - var format = image.RawFormat; - var codec = ImageCodecInfo.GetImageDecoders().First(c => c.FormatID == format.Guid); - return codec.MimeType; - } - - internal static IEnumerable GenerateMediaThumbnails( - IFileSystem fs, - string fileName, - string extension, - Image originalImage, - IEnumerable additionalThumbSizes) - { - // Make default thumbnails - var thumbs = new List - { - Resize(fs, fileName, extension, 100, "thumb", originalImage), - Resize(fs, fileName, extension, 500, "big-thumb", originalImage) - }; - - //make custom ones - foreach (var thumb in additionalThumbSizes.Where(x => x > 0).Distinct()) - { - thumbs.Add(Resize(fs, fileName, extension, thumb, string.Format("thumb_{0}", thumb), originalImage)); - } - - return thumbs; - } - - private static ResizedImage Resize(IFileSystem fileSystem, string path, string extension, int maxWidthHeight, string fileNameAddition, Image originalImage) - { - var fileNameThumb = String.IsNullOrEmpty(fileNameAddition) - ? string.Format("{0}_UMBRACOSYSTHUMBNAIL.jpg", path.Substring(0, path.LastIndexOf("."))) - : string.Format("{0}_{1}.jpg", path.Substring(0, path.LastIndexOf(".")), fileNameAddition); - - var thumb = GenerateThumbnail( - originalImage, - maxWidthHeight, - fileNameThumb, - extension, - fileSystem); - - return thumb; - } - - internal static ResizedImage GenerateThumbnail(Image image, int maxWidthHeight, string thumbnailFileName, string extension, IFileSystem fs) - { - return GenerateThumbnail(image, maxWidthHeight, -1, -1, thumbnailFileName, extension, fs); - } - - internal static ResizedImage GenerateThumbnail(Image image, int fixedWidth, int fixedHeight, string thumbnailFileName, string extension, IFileSystem fs) - { - return GenerateThumbnail(image, -1, fixedWidth, fixedHeight, thumbnailFileName, extension, fs); - } - - private static ResizedImage GenerateThumbnail(Image image, int maxWidthHeight, int fixedWidth, int fixedHeight, string thumbnailFileName, string extension, IFileSystem fs) - { - // Generate thumbnail - float f = 1; - if (maxWidthHeight >= 0) - { - var fx = (float)image.Size.Width / maxWidthHeight; - var fy = (float)image.Size.Height / maxWidthHeight; - - // must fit in thumbnail size - f = Math.Max(fx, fy); - } - - //depending on if we are doing fixed width resizing or not. - fixedWidth = (maxWidthHeight > 0) ? image.Width : fixedWidth; - fixedHeight = (maxWidthHeight > 0) ? image.Height : fixedHeight; - - var widthTh = (int)Math.Round(fixedWidth / f); - var heightTh = (int)Math.Round(fixedHeight / f); - - // fixes for empty width or height - if (widthTh == 0) - widthTh = 1; - if (heightTh == 0) - heightTh = 1; - - // Create new image with best quality settings - using (var bp = new Bitmap(widthTh, heightTh)) - { - using (var g = Graphics.FromImage(bp)) - { - g.SmoothingMode = SmoothingMode.HighQuality; - g.InterpolationMode = InterpolationMode.HighQualityBicubic; - g.PixelOffsetMode = PixelOffsetMode.HighQuality; - g.CompositingQuality = CompositingQuality.HighQuality; - - // Copy the old image to the new and resized - var rect = new Rectangle(0, 0, widthTh, heightTh); - g.DrawImage(image, rect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel); - - // Copy metadata - var imageEncoders = ImageCodecInfo.GetImageEncoders(); - - var codec = extension.ToLower() == "png" || extension.ToLower() == "gif" - ? imageEncoders.Single(t => t.MimeType.Equals("image/png")) - : imageEncoders.Single(t => t.MimeType.Equals("image/jpeg")); - - // Set compresion ratio to 90% - var ep = new EncoderParameters(); - ep.Param[0] = new EncoderParameter(Encoder.Quality, 90L); - - // Save the new image using the dimensions of the image - var newFileName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", string.Format("{0}x{1}", widthTh, heightTh)); - using (var ms = new MemoryStream()) - { - bp.Save(ms, codec, ep); - ms.Seek(0, 0); - - fs.AddFile(newFileName, ms); - } - - return new ResizedImage(widthTh, heightTh, newFileName); - } - } - } - - } -} +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; + + +namespace Umbraco.Core.Media +{ + /// + /// A helper class used for imaging + /// + internal static class ImageHelper + { + public static string GetMimeType(this Image image) + { + var format = image.RawFormat; + var codec = ImageCodecInfo.GetImageDecoders().First(c => c.FormatID == format.Guid); + return codec.MimeType; + } + + internal static IEnumerable GenerateMediaThumbnails( + IFileSystem fs, + string fileName, + string extension, + Image originalImage, + IEnumerable additionalThumbSizes) + { + // Make default thumbnails + var thumbs = new List + { + Resize(fs, fileName, extension, 100, "thumb", originalImage), + Resize(fs, fileName, extension, 500, "big-thumb", originalImage) + }; + + //make custom ones + foreach (var thumb in additionalThumbSizes.Where(x => x > 0).Distinct()) + { + thumbs.Add(Resize(fs, fileName, extension, thumb, string.Format("thumb_{0}", thumb), originalImage)); + } + + return thumbs; + } + + private static ResizedImage Resize(IFileSystem fileSystem, string path, string extension, int maxWidthHeight, string fileNameAddition, Image originalImage) + { + var fileNameThumb = String.IsNullOrEmpty(fileNameAddition) + ? string.Format("{0}_UMBRACOSYSTHUMBNAIL.jpg", path.Substring(0, path.LastIndexOf("."))) + : string.Format("{0}_{1}.jpg", path.Substring(0, path.LastIndexOf(".")), fileNameAddition); + + var thumb = GenerateThumbnail( + originalImage, + maxWidthHeight, + fileNameThumb, + extension, + fileSystem); + + return thumb; + } + + internal static ResizedImage GenerateThumbnail(Image image, int maxWidthHeight, string thumbnailFileName, string extension, IFileSystem fs) + { + return GenerateThumbnail(image, maxWidthHeight, -1, -1, thumbnailFileName, extension, fs); + } + + internal static ResizedImage GenerateThumbnail(Image image, int fixedWidth, int fixedHeight, string thumbnailFileName, string extension, IFileSystem fs) + { + return GenerateThumbnail(image, -1, fixedWidth, fixedHeight, thumbnailFileName, extension, fs); + } + + private static ResizedImage GenerateThumbnail(Image image, int maxWidthHeight, int fixedWidth, int fixedHeight, string thumbnailFileName, string extension, IFileSystem fs) + { + // Generate thumbnail + float f = 1; + if (maxWidthHeight >= 0) + { + var fx = (float)image.Size.Width / maxWidthHeight; + var fy = (float)image.Size.Height / maxWidthHeight; + + // must fit in thumbnail size + f = Math.Max(fx, fy); + } + + //depending on if we are doing fixed width resizing or not. + fixedWidth = (maxWidthHeight > 0) ? image.Width : fixedWidth; + fixedHeight = (maxWidthHeight > 0) ? image.Height : fixedHeight; + + var widthTh = (int)Math.Round(fixedWidth / f); + var heightTh = (int)Math.Round(fixedHeight / f); + + // fixes for empty width or height + if (widthTh == 0) + widthTh = 1; + if (heightTh == 0) + heightTh = 1; + + // Create new image with best quality settings + using (var bp = new Bitmap(widthTh, heightTh)) + { + using (var g = Graphics.FromImage(bp)) + { + g.SmoothingMode = SmoothingMode.HighQuality; + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.PixelOffsetMode = PixelOffsetMode.HighQuality; + g.CompositingQuality = CompositingQuality.HighQuality; + + // Copy the old image to the new and resized + var rect = new Rectangle(0, 0, widthTh, heightTh); + g.DrawImage(image, rect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel); + + // Copy metadata + var imageEncoders = ImageCodecInfo.GetImageEncoders(); + + var codec = extension.ToLower() == "png" || extension.ToLower() == "gif" + ? imageEncoders.Single(t => t.MimeType.Equals("image/png")) + : imageEncoders.Single(t => t.MimeType.Equals("image/jpeg")); + + // Set compresion ratio to 90% + var ep = new EncoderParameters(); + ep.Param[0] = new EncoderParameter(Encoder.Quality, 90L); + + // Save the new image using the dimensions of the image + var newFileName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", string.Format("{0}x{1}", widthTh, heightTh)); + using (var ms = new MemoryStream()) + { + bp.Save(ms, codec, ep); + ms.Seek(0, 0); + + fs.AddFile(newFileName, ms); + } + + return new ResizedImage(widthTh, heightTh, newFileName); + } + } + } + + } +} diff --git a/src/Umbraco.Core/Models/IMacro.cs b/src/Umbraco.Core/Models/IMacro.cs index 7ca3d8d18f..f28160c345 100644 --- a/src/Umbraco.Core/Models/IMacro.cs +++ b/src/Umbraco.Core/Models/IMacro.cs @@ -1,93 +1,93 @@ -using System.Collections.Generic; -using System.Runtime.Serialization; -using Umbraco.Core.Models.EntityBase; - -namespace Umbraco.Core.Models -{ - /// - /// Defines a Macro - /// - public interface IMacro : IAggregateRoot - { - /// - /// Gets or sets the alias of the Macro - /// - [DataMember] - string Alias { get; set; } - - /// - /// Gets or sets the name of the Macro - /// - [DataMember] - string Name { get; set; } - - /// - /// Gets or sets a boolean indicating whether the Macro can be used in an Editor - /// - [DataMember] - bool UseInEditor { get; set; } - - /// - /// Gets or sets the Cache Duration for the Macro - /// - [DataMember] - int CacheDuration { get; set; } - - /// - /// Gets or sets a boolean indicating whether the Macro should be Cached by Page - /// - [DataMember] - bool CacheByPage { get; set; } - - /// - /// Gets or sets a boolean indicating whether the Macro should be Cached Personally - /// - [DataMember] - bool CacheByMember { get; set; } - - /// - /// Gets or sets a boolean indicating whether the Macro should be rendered in an Editor - /// - [DataMember] - bool DontRender { get; set; } - - /// - /// Gets or sets the path to user control or the Control Type to render - /// - [DataMember] - string ControlType { get; set; } - - /// - /// Gets or sets the name of the assembly, which should be used by the Macro - /// - /// Will usually only be filled if the ScriptFile is a Usercontrol - [DataMember] - string ControlAssembly { get; set; } - - /// - /// Gets or set the path to the Python file in use - /// - /// Optional: Can only be one of three Script, Python or Xslt - [DataMember] - string ScriptPath { get; set; } - - /// - /// Gets or sets the path to the Xslt file in use - /// - /// Optional: Can only be one of three Script, Python or Xslt - [DataMember] - string XsltPath { get; set; } - - /// - /// Gets or sets a list of Macro Properties - /// - [DataMember] - MacroPropertyCollection Properties { get; } - - ///// - ///// Returns an enum based on the properties on the Macro - ///// - ///// - //MacroTypes MacroType(); - } +using System.Collections.Generic; +using System.Runtime.Serialization; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models +{ + /// + /// Defines a Macro + /// + public interface IMacro : IAggregateRoot + { + /// + /// Gets or sets the alias of the Macro + /// + [DataMember] + string Alias { get; set; } + + /// + /// Gets or sets the name of the Macro + /// + [DataMember] + string Name { get; set; } + + /// + /// Gets or sets a boolean indicating whether the Macro can be used in an Editor + /// + [DataMember] + bool UseInEditor { get; set; } + + /// + /// Gets or sets the Cache Duration for the Macro + /// + [DataMember] + int CacheDuration { get; set; } + + /// + /// Gets or sets a boolean indicating whether the Macro should be Cached by Page + /// + [DataMember] + bool CacheByPage { get; set; } + + /// + /// Gets or sets a boolean indicating whether the Macro should be Cached Personally + /// + [DataMember] + bool CacheByMember { get; set; } + + /// + /// Gets or sets a boolean indicating whether the Macro should be rendered in an Editor + /// + [DataMember] + bool DontRender { get; set; } + + /// + /// Gets or sets the path to user control or the Control Type to render + /// + [DataMember] + string ControlType { get; set; } + + /// + /// Gets or sets the name of the assembly, which should be used by the Macro + /// + /// Will usually only be filled if the ScriptFile is a Usercontrol + [DataMember] + string ControlAssembly { get; set; } + + /// + /// Gets or set the path to the Python file in use + /// + /// Optional: Can only be one of three Script, Python or Xslt + [DataMember] + string ScriptPath { get; set; } + + /// + /// Gets or sets the path to the Xslt file in use + /// + /// Optional: Can only be one of three Script, Python or Xslt + [DataMember] + string XsltPath { get; set; } + + /// + /// Gets or sets a list of Macro Properties + /// + [DataMember] + MacroPropertyCollection Properties { get; } + + ///// + ///// Returns an enum based on the properties on the Macro + ///// + ///// + //MacroTypes MacroType(); + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/IMacroProperty.cs b/src/Umbraco.Core/Models/IMacroProperty.cs index 7e8e60690f..551d60304c 100644 --- a/src/Umbraco.Core/Models/IMacroProperty.cs +++ b/src/Umbraco.Core/Models/IMacroProperty.cs @@ -1,38 +1,38 @@ -using System.Runtime.Serialization; -using Umbraco.Core.Models.EntityBase; - -namespace Umbraco.Core.Models -{ - /// - /// Defines a Property for a Macro - /// - public interface IMacroProperty : IValueObject - { - [DataMember] - int Id { get; set; } - - /// - /// Gets or sets the Alias of the Property - /// - [DataMember] - string Alias { get; set; } - - /// - /// Gets or sets the Name of the Property - /// - [DataMember] - string Name { get; set; } - - /// - /// Gets or sets the Sort Order of the Property - /// - [DataMember] - int SortOrder { get; set; } - - /// - /// Gets or sets the parameter editor alias - /// - [DataMember] - string EditorAlias { get; set; } - } +using System.Runtime.Serialization; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models +{ + /// + /// Defines a Property for a Macro + /// + public interface IMacroProperty : IValueObject + { + [DataMember] + int Id { get; set; } + + /// + /// Gets or sets the Alias of the Property + /// + [DataMember] + string Alias { get; set; } + + /// + /// Gets or sets the Name of the Property + /// + [DataMember] + string Name { get; set; } + + /// + /// Gets or sets the Sort Order of the Property + /// + [DataMember] + int SortOrder { get; set; } + + /// + /// Gets or sets the parameter editor alias + /// + [DataMember] + string EditorAlias { get; set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/IMacroPropertyType.cs b/src/Umbraco.Core/Models/IMacroPropertyType.cs index 55002a3d82..7c4bc0057f 100644 --- a/src/Umbraco.Core/Models/IMacroPropertyType.cs +++ b/src/Umbraco.Core/Models/IMacroPropertyType.cs @@ -1,28 +1,28 @@ -namespace Umbraco.Core.Models -{ - /// - /// Defines a PropertyType (plugin) for a Macro - /// - internal interface IMacroPropertyType - { - /// - /// Gets the unique Alias of the Property Type - /// - string Alias { get; } - - /// - /// Gets the name of the Assembly used to render the Property Type - /// - string RenderingAssembly { get; } - - /// - /// Gets the name of the Type used to render the Property Type - /// - string RenderingType { get; } - - /// - /// Gets the Base Type for storing the PropertyType (Int32, String, Boolean) - /// - MacroPropertyTypeBaseTypes BaseType { get; } - } +namespace Umbraco.Core.Models +{ + /// + /// Defines a PropertyType (plugin) for a Macro + /// + internal interface IMacroPropertyType + { + /// + /// Gets the unique Alias of the Property Type + /// + string Alias { get; } + + /// + /// Gets the name of the Assembly used to render the Property Type + /// + string RenderingAssembly { get; } + + /// + /// Gets the name of the Type used to render the Property Type + /// + string RenderingType { get; } + + /// + /// Gets the Base Type for storing the PropertyType (Int32, String, Boolean) + /// + MacroPropertyTypeBaseTypes BaseType { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/IMember.cs b/src/Umbraco.Core/Models/IMember.cs index 0b347a2515..edacd0b62b 100644 --- a/src/Umbraco.Core/Models/IMember.cs +++ b/src/Umbraco.Core/Models/IMember.cs @@ -1,114 +1,114 @@ -using System; - -namespace Umbraco.Core.Models -{ - public interface IMember : IContentBase - { - /// - /// Gets or sets the Username - /// - string Username { get; set; } - - /// - /// Gets or sets the Email - /// - string Email { get; set; } - - /// - /// Gets or sets the Password - /// - string Password { get; set; } - - /// - /// Gets or sets the Password Question - /// - /// - /// Alias: umbracoPasswordRetrievalQuestionPropertyTypeAlias - /// Part of the standard properties collection. - /// - string PasswordQuestion { get; set; } - - /// - /// Gets or sets the Password Answer - /// - /// - /// Alias: umbracoPasswordRetrievalAnswerPropertyTypeAlias - /// Part of the standard properties collection. - /// - string PasswordAnswer { get; set; } - - /// - /// Gets or set the comments for the member - /// - /// - /// Alias: umbracoCommentPropertyTypeAlias - /// Part of the standard properties collection. - /// - string Comments { get; set; } - - /// - /// Gets or sets a boolean indicating whether the Member is approved - /// - /// - /// Alias: umbracoApprovePropertyTypeAlias - /// Part of the standard properties collection. - /// - bool IsApproved { get; set; } - - /// - /// Gets or sets a boolean indicating whether the Member is locked out - /// - /// - /// Alias: umbracoLockPropertyTypeAlias - /// Part of the standard properties collection. - /// - bool IsLockedOut { get; set; } - - /// - /// Gets or sets the date for last login - /// - /// - /// Alias: umbracoLastLoginPropertyTypeAlias - /// Part of the standard properties collection. - /// - DateTime LastLoginDate { get; set; } - - /// - /// Gest or sets the date for last password change - /// - /// - /// Alias: umbracoMemberLastPasswordChange - /// Part of the standard properties collection. - /// - DateTime LastPasswordChangeDate { get; set; } - - /// - /// Gets or sets the date for when Member was locked out - /// - /// - /// Alias: umbracoMemberLastLockout - /// Part of the standard properties collection. - /// - DateTime LastLockoutDate { get; set; } - - /// - /// Gets or sets the number of failed password attempts. - /// This is the number of times the password was entered incorrectly upon login. - /// - /// - /// Alias: umbracoFailedPasswordAttemptsPropertyTypeAlias - /// Part of the standard properties collection. - /// - int FailedPasswordAttempts { get; set; } - - /// - /// String alias of the default ContentType - /// - string ContentTypeAlias { get; } - - /// - /// Gets the ContentType used by this content object - /// - IMemberType ContentType { get; } - } +using System; + +namespace Umbraco.Core.Models +{ + public interface IMember : IContentBase + { + /// + /// Gets or sets the Username + /// + string Username { get; set; } + + /// + /// Gets or sets the Email + /// + string Email { get; set; } + + /// + /// Gets or sets the Password + /// + string Password { get; set; } + + /// + /// Gets or sets the Password Question + /// + /// + /// Alias: umbracoPasswordRetrievalQuestionPropertyTypeAlias + /// Part of the standard properties collection. + /// + string PasswordQuestion { get; set; } + + /// + /// Gets or sets the Password Answer + /// + /// + /// Alias: umbracoPasswordRetrievalAnswerPropertyTypeAlias + /// Part of the standard properties collection. + /// + string PasswordAnswer { get; set; } + + /// + /// Gets or set the comments for the member + /// + /// + /// Alias: umbracoCommentPropertyTypeAlias + /// Part of the standard properties collection. + /// + string Comments { get; set; } + + /// + /// Gets or sets a boolean indicating whether the Member is approved + /// + /// + /// Alias: umbracoApprovePropertyTypeAlias + /// Part of the standard properties collection. + /// + bool IsApproved { get; set; } + + /// + /// Gets or sets a boolean indicating whether the Member is locked out + /// + /// + /// Alias: umbracoLockPropertyTypeAlias + /// Part of the standard properties collection. + /// + bool IsLockedOut { get; set; } + + /// + /// Gets or sets the date for last login + /// + /// + /// Alias: umbracoLastLoginPropertyTypeAlias + /// Part of the standard properties collection. + /// + DateTime LastLoginDate { get; set; } + + /// + /// Gest or sets the date for last password change + /// + /// + /// Alias: umbracoMemberLastPasswordChange + /// Part of the standard properties collection. + /// + DateTime LastPasswordChangeDate { get; set; } + + /// + /// Gets or sets the date for when Member was locked out + /// + /// + /// Alias: umbracoMemberLastLockout + /// Part of the standard properties collection. + /// + DateTime LastLockoutDate { get; set; } + + /// + /// Gets or sets the number of failed password attempts. + /// This is the number of times the password was entered incorrectly upon login. + /// + /// + /// Alias: umbracoFailedPasswordAttemptsPropertyTypeAlias + /// Part of the standard properties collection. + /// + int FailedPasswordAttempts { get; set; } + + /// + /// String alias of the default ContentType + /// + string ContentTypeAlias { get; } + + /// + /// Gets the ContentType used by this content object + /// + IMemberType ContentType { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/IMemberType.cs b/src/Umbraco.Core/Models/IMemberType.cs index 8f39e96801..878cc24334 100644 --- a/src/Umbraco.Core/Models/IMemberType.cs +++ b/src/Umbraco.Core/Models/IMemberType.cs @@ -1,36 +1,36 @@ -namespace Umbraco.Core.Models -{ - /// - /// Defines a MemberType, which Member is based on - /// - public interface IMemberType : IContentTypeComposition - { - /// - /// Gets a boolean indicating whether a Property is editable by the Member. - /// - /// PropertyType Alias of the Property to check - /// - bool MemberCanEditProperty(string propertyTypeAlias); - - /// - /// Gets a boolean indicating whether a Property is visible on the Members profile. - /// - /// PropertyType Alias of the Property to check - /// - bool MemberCanViewProperty(string propertyTypeAlias); - - /// - /// Sets a boolean indicating whether a Property is editable by the Member. - /// - /// PropertyType Alias of the Property to set - /// Boolean value, true or false - void SetMemberCanEditProperty(string propertyTypeAlias, bool value); - - /// - /// Sets a boolean indicating whether a Property is visible on the Members profile. - /// - /// PropertyType Alias of the Property to set - /// Boolean value, true or false - void SetMemberCanViewProperty(string propertyTypeAlias, bool value); - } +namespace Umbraco.Core.Models +{ + /// + /// Defines a MemberType, which Member is based on + /// + public interface IMemberType : IContentTypeComposition + { + /// + /// Gets a boolean indicating whether a Property is editable by the Member. + /// + /// PropertyType Alias of the Property to check + /// + bool MemberCanEditProperty(string propertyTypeAlias); + + /// + /// Gets a boolean indicating whether a Property is visible on the Members profile. + /// + /// PropertyType Alias of the Property to check + /// + bool MemberCanViewProperty(string propertyTypeAlias); + + /// + /// Sets a boolean indicating whether a Property is editable by the Member. + /// + /// PropertyType Alias of the Property to set + /// Boolean value, true or false + void SetMemberCanEditProperty(string propertyTypeAlias, bool value); + + /// + /// Sets a boolean indicating whether a Property is visible on the Members profile. + /// + /// PropertyType Alias of the Property to set + /// Boolean value, true or false + void SetMemberCanViewProperty(string propertyTypeAlias, bool value); + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/IPublishedProperty.cs b/src/Umbraco.Core/Models/IPublishedProperty.cs index f6afa1f05e..a50163bc5c 100644 --- a/src/Umbraco.Core/Models/IPublishedProperty.cs +++ b/src/Umbraco.Core/Models/IPublishedProperty.cs @@ -1,60 +1,60 @@ -namespace Umbraco.Core.Models -{ - /// - /// Represents a property of an IPublishedContent. - /// - public interface IPublishedProperty - { - /// - /// Gets the alias of the property. - /// - string PropertyTypeAlias { get; } - - /// - /// Gets a value indicating whether the property has a value. - /// - /// - /// This is somewhat implementation-dependent -- depending on whatever IPublishedCache considers - /// a missing value. - /// The XmlPublishedCache raw values are strings, and it will consider missing, null or empty (and - /// that includes whitespace-only) strings as "no value". - /// Other caches that get their raw value from the database would consider that a property has "no - /// value" if it is missing, null, or an empty string (including whitespace-only). - /// - bool HasValue { get; } - - /// - /// Gets the data value of the property. - /// - /// - /// The data value is whatever was passed to the property when it was instanciated, and it is - /// somewhat implementation-dependent -- depending on how the IPublishedCache is implemented. - /// The XmlPublishedCache raw values are strings exclusively since they come from the Xml cache. - /// For other caches that get their raw value from the database, it would be either a string, - /// an integer (Int32), or a date and time (DateTime). - /// If you're using that value, you're probably wrong, unless you're doing some internal - /// Umbraco stuff. - /// - object DataValue { get; } - - /// - /// Gets the object value of the property. - /// - /// - /// The value is what you want to use when rendering content in an MVC view ie in C#. - /// It can be null, or any type of CLR object. - /// It has been fully prepared and processed by the appropriate converter. - /// - object Value { get; } - - /// - /// Gets the XPath value of the property. - /// - /// - /// The XPath value is what you want to use when navigating content via XPath eg in the XSLT engine. - /// It must be either null, or a string, or an XPathNavigator. - /// It has been fully prepared and processed by the appropriate converter. - /// - object XPathValue { get; } - } +namespace Umbraco.Core.Models +{ + /// + /// Represents a property of an IPublishedContent. + /// + public interface IPublishedProperty + { + /// + /// Gets the alias of the property. + /// + string PropertyTypeAlias { get; } + + /// + /// Gets a value indicating whether the property has a value. + /// + /// + /// This is somewhat implementation-dependent -- depending on whatever IPublishedCache considers + /// a missing value. + /// The XmlPublishedCache raw values are strings, and it will consider missing, null or empty (and + /// that includes whitespace-only) strings as "no value". + /// Other caches that get their raw value from the database would consider that a property has "no + /// value" if it is missing, null, or an empty string (including whitespace-only). + /// + bool HasValue { get; } + + /// + /// Gets the data value of the property. + /// + /// + /// The data value is whatever was passed to the property when it was instanciated, and it is + /// somewhat implementation-dependent -- depending on how the IPublishedCache is implemented. + /// The XmlPublishedCache raw values are strings exclusively since they come from the Xml cache. + /// For other caches that get their raw value from the database, it would be either a string, + /// an integer (Int32), or a date and time (DateTime). + /// If you're using that value, you're probably wrong, unless you're doing some internal + /// Umbraco stuff. + /// + object DataValue { get; } + + /// + /// Gets the object value of the property. + /// + /// + /// The value is what you want to use when rendering content in an MVC view ie in C#. + /// It can be null, or any type of CLR object. + /// It has been fully prepared and processed by the appropriate converter. + /// + object Value { get; } + + /// + /// Gets the XPath value of the property. + /// + /// + /// The XPath value is what you want to use when navigating content via XPath eg in the XSLT engine. + /// It must be either null, or a string, or an XPathNavigator. + /// It has been fully prepared and processed by the appropriate converter. + /// + object XPathValue { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/IRelation.cs b/src/Umbraco.Core/Models/IRelation.cs index b37357f8bc..2c24fdfad3 100644 --- a/src/Umbraco.Core/Models/IRelation.cs +++ b/src/Umbraco.Core/Models/IRelation.cs @@ -1,38 +1,38 @@ -using System.Runtime.Serialization; -using Umbraco.Core.Models.EntityBase; - -namespace Umbraco.Core.Models -{ - public interface IRelation : IAggregateRoot - { - /// - /// Gets or sets the Parent Id of the Relation (Source) - /// - [DataMember] - int ParentId { get; set; } - - /// - /// Gets or sets the Child Id of the Relation (Destination) - /// - [DataMember] - int ChildId { get; set; } - - /// - /// Gets or sets the for the Relation - /// - [DataMember] - IRelationType RelationType { get; set; } - - /// - /// Gets or sets a comment for the Relation - /// - [DataMember] - string Comment { get; set; } - - /// - /// Gets the Id of the that this Relation is based on. - /// - [IgnoreDataMember] - int RelationTypeId { get; } - } +using System.Runtime.Serialization; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models +{ + public interface IRelation : IAggregateRoot + { + /// + /// Gets or sets the Parent Id of the Relation (Source) + /// + [DataMember] + int ParentId { get; set; } + + /// + /// Gets or sets the Child Id of the Relation (Destination) + /// + [DataMember] + int ChildId { get; set; } + + /// + /// Gets or sets the for the Relation + /// + [DataMember] + IRelationType RelationType { get; set; } + + /// + /// Gets or sets a comment for the Relation + /// + [DataMember] + string Comment { get; set; } + + /// + /// Gets the Id of the that this Relation is based on. + /// + [IgnoreDataMember] + int RelationTypeId { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/IRelationType.cs b/src/Umbraco.Core/Models/IRelationType.cs index 8fc12eb6a1..adb751080e 100644 --- a/src/Umbraco.Core/Models/IRelationType.cs +++ b/src/Umbraco.Core/Models/IRelationType.cs @@ -1,41 +1,41 @@ -using System; -using System.Runtime.Serialization; -using Umbraco.Core.Models.EntityBase; - -namespace Umbraco.Core.Models -{ - public interface IRelationType : IAggregateRoot - { - /// - /// Gets or sets the Name of the RelationType - /// - [DataMember] - string Name { get; set; } - - /// - /// Gets or sets the Alias of the RelationType - /// - [DataMember] - string Alias { get; set; } - - /// - /// Gets or sets a boolean indicating whether the RelationType is Bidirectional (true) or Parent to Child (false) - /// - [DataMember] - bool IsBidirectional { get; set; } - - /// - /// Gets or sets the Parents object type id - /// - /// Corresponds to the NodeObjectType in the umbracoNode table - [DataMember] - Guid ParentObjectType { get; set; } - - /// - /// Gets or sets the Childs object type id - /// - /// Corresponds to the NodeObjectType in the umbracoNode table - [DataMember] - Guid ChildObjectType { get; set; } - } +using System; +using System.Runtime.Serialization; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models +{ + public interface IRelationType : IAggregateRoot + { + /// + /// Gets or sets the Name of the RelationType + /// + [DataMember] + string Name { get; set; } + + /// + /// Gets or sets the Alias of the RelationType + /// + [DataMember] + string Alias { get; set; } + + /// + /// Gets or sets a boolean indicating whether the RelationType is Bidirectional (true) or Parent to Child (false) + /// + [DataMember] + bool IsBidirectional { get; set; } + + /// + /// Gets or sets the Parents object type id + /// + /// Corresponds to the NodeObjectType in the umbracoNode table + [DataMember] + Guid ParentObjectType { get; set; } + + /// + /// Gets or sets the Childs object type id + /// + /// Corresponds to the NodeObjectType in the umbracoNode table + [DataMember] + Guid ChildObjectType { get; set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/ITag.cs b/src/Umbraco.Core/Models/ITag.cs index 05990aa6a0..8c1c1aa5e0 100644 --- a/src/Umbraco.Core/Models/ITag.cs +++ b/src/Umbraco.Core/Models/ITag.cs @@ -1,17 +1,17 @@ -using System.Runtime.Serialization; -using Umbraco.Core.Models.EntityBase; - -namespace Umbraco.Core.Models -{ - public interface ITag : IAggregateRoot - { - [DataMember] - string Text { get; set; } - - [DataMember] - string Group { get; set; } - - //TODO: enable this at some stage - //int ParentId { get; set; } - } +using System.Runtime.Serialization; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models +{ + public interface ITag : IAggregateRoot + { + [DataMember] + string Text { get; set; } + + [DataMember] + string Group { get; set; } + + //TODO: enable this at some stage + //int ParentId { get; set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Macro.cs b/src/Umbraco.Core/Models/Macro.cs index a0f0bd9651..914847aea2 100644 --- a/src/Umbraco.Core/Models/Macro.cs +++ b/src/Umbraco.Core/Models/Macro.cs @@ -1,403 +1,403 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Linq; -using System.Reflection; -using System.Runtime.Serialization; -using System.Text.RegularExpressions; -using Umbraco.Core.IO; -using Umbraco.Core.Models.EntityBase; - -namespace Umbraco.Core.Models -{ - /// - /// Represents a Macro - /// - [Serializable] - [DataContract(IsReference = true)] - internal class Macro : Entity, IMacro - { - public Macro() - { - _properties = new MacroPropertyCollection(); - _properties.CollectionChanged += PropertiesChanged; - _addedProperties = new List(); - _removedProperties = new List(); - } - - /// - /// Creates an item with pre-filled properties - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Macro(int id, bool useInEditor, int cacheDuration, string @alias, string name, string controlType, string controlAssembly, string xsltPath, bool cacheByPage, bool cacheByMember, bool dontRender, string scriptPath) - : this() - { - Id = id; - UseInEditor = useInEditor; - CacheDuration = cacheDuration; - Alias = alias; - Name = name; - ControlType = controlType; - ControlAssembly = controlAssembly; - XsltPath = xsltPath; - CacheByPage = cacheByPage; - CacheByMember = cacheByMember; - DontRender = dontRender; - ScriptPath = scriptPath; - } - - /// - /// Creates an instance for persisting a new item - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Macro(string @alias, string name, - string controlType = "", - string controlAssembly = "", - string xsltPath = "", - string scriptPath = "", - bool cacheByPage = false, - bool cacheByMember = false, - bool dontRender = true, - bool useInEditor = false, - int cacheDuration = 0) - : this() - { - UseInEditor = useInEditor; - CacheDuration = cacheDuration; - Alias = alias; - Name = name; - ControlType = controlType; - ControlAssembly = controlAssembly; - XsltPath = xsltPath; - CacheByPage = cacheByPage; - CacheByMember = cacheByMember; - DontRender = dontRender; - ScriptPath = scriptPath; - } - - private string _alias; - private string _name; - private bool _useInEditor; - private int _cacheDuration; - private bool _cacheByPage; - private bool _cacheByMember; - private bool _dontRender; - private string _scriptFile; - private string _scriptAssembly; - private string _python; - private string _xslt; - private readonly MacroPropertyCollection _properties; - private readonly List _addedProperties; - private readonly List _removedProperties; - - private static readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo(x => x.Alias); - private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); - private static readonly PropertyInfo UseInEditorSelector = ExpressionHelper.GetPropertyInfo(x => x.UseInEditor); - private static readonly PropertyInfo CacheDurationSelector = ExpressionHelper.GetPropertyInfo(x => x.CacheDuration); - private static readonly PropertyInfo CacheByPageSelector = ExpressionHelper.GetPropertyInfo(x => x.CacheByPage); - private static readonly PropertyInfo CacheByMemberSelector = ExpressionHelper.GetPropertyInfo(x => x.CacheByMember); - private static readonly PropertyInfo DontRenderSelector = ExpressionHelper.GetPropertyInfo(x => x.DontRender); - private static readonly PropertyInfo ControlPathSelector = ExpressionHelper.GetPropertyInfo(x => x.ControlType); - private static readonly PropertyInfo ControlAssemblySelector = ExpressionHelper.GetPropertyInfo(x => x.ControlAssembly); - private static readonly PropertyInfo ScriptPathSelector = ExpressionHelper.GetPropertyInfo(x => x.ScriptPath); - private static readonly PropertyInfo XsltPathSelector = ExpressionHelper.GetPropertyInfo(x => x.XsltPath); - private static readonly PropertyInfo PropertiesSelector = ExpressionHelper.GetPropertyInfo(x => x.Properties); - - void PropertiesChanged(object sender, NotifyCollectionChangedEventArgs e) - { - OnPropertyChanged(PropertiesSelector); - - if (e.Action == NotifyCollectionChangedAction.Add) - { - //listen for changes - var prop = e.NewItems.Cast().First(); - prop.PropertyChanged += PropertyDataChanged; - - var alias = prop.Alias; - - //remove from the removed/added props (since people could add/remove all they want in one request) - _removedProperties.RemoveAll(s => s == alias); - _addedProperties.RemoveAll(s => s == alias); - - //add to the added props - _addedProperties.Add(alias); - } - else if (e.Action == NotifyCollectionChangedAction.Remove) - { - //remove listening for changes - var prop = e.OldItems.Cast().First(); - prop.PropertyChanged -= PropertyDataChanged; - - var alias = prop.Alias; - - //remove from the removed/added props (since people could add/remove all they want in one request) - _removedProperties.RemoveAll(s => s == alias); - _addedProperties.RemoveAll(s => s == alias); - - //add to the added props - _removedProperties.Add(alias); - } - } - - /// - /// When some data of a property has changed ensure our Properties flag is dirty - /// - /// - /// - void PropertyDataChanged(object sender, PropertyChangedEventArgs e) - { - OnPropertyChanged(PropertiesSelector); - } - - public override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties) - { - _addedProperties.Clear(); - _removedProperties.Clear(); - base.ResetDirtyProperties(rememberPreviouslyChangedProperties); - foreach (var prop in Properties) - { - ((TracksChangesEntityBase)prop).ResetDirtyProperties(rememberPreviouslyChangedProperties); - } - } - - /// - /// Used internally to check if we need to add a section in the repository to the db - /// - internal IEnumerable AddedProperties - { - get { return _addedProperties; } - } - - /// - /// Used internally to check if we need to remove a section in the repository to the db - /// - internal IEnumerable RemovedProperties - { - get { return _removedProperties; } - } - - /// - /// Gets or sets the alias of the Macro - /// - [DataMember] - public string Alias - { - get { return _alias; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _alias = value; - return _alias; - }, _alias, AliasSelector); - } - } - - /// - /// Gets or sets the name of the Macro - /// - [DataMember] - public string Name - { - get { return _name; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _name = value; - return _name; - }, _name, NameSelector); - } - } - - /// - /// Gets or sets a boolean indicating whether the Macro can be used in an Editor - /// - [DataMember] - public bool UseInEditor - { - get { return _useInEditor; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _useInEditor = value; - return _useInEditor; - }, _useInEditor, UseInEditorSelector); - } - } - - /// - /// Gets or sets the Cache Duration for the Macro - /// - [DataMember] - public int CacheDuration - { - get { return _cacheDuration; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _cacheDuration = value; - return _cacheDuration; - }, _cacheDuration, CacheDurationSelector); - } - } - - /// - /// Gets or sets a boolean indicating whether the Macro should be Cached by Page - /// - [DataMember] - public bool CacheByPage - { - get { return _cacheByPage; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _cacheByPage = value; - return _cacheByPage; - }, _cacheByPage, CacheByPageSelector); - } - } - - /// - /// Gets or sets a boolean indicating whether the Macro should be Cached Personally - /// - [DataMember] - public bool CacheByMember - { - get { return _cacheByMember; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _cacheByMember = value; - return _cacheByMember; - }, _cacheByMember, CacheByMemberSelector); - } - } - - /// - /// Gets or sets a boolean indicating whether the Macro should be rendered in an Editor - /// - [DataMember] - public bool DontRender - { - get { return _dontRender; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _dontRender = value; - return _dontRender; - }, _dontRender, DontRenderSelector); - } - } - - /// - /// Gets or sets the path to user control or the Control Type to render - /// - [DataMember] - public string ControlType - { - get { return _scriptFile; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _scriptFile = value; - return _scriptFile; - }, _scriptFile, ControlPathSelector); - } - } - - /// - /// Gets or sets the name of the assembly, which should be used by the Macro - /// - /// Will usually only be filled if the ControlType is a Usercontrol - [DataMember] - public string ControlAssembly - { - get { return _scriptAssembly; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _scriptAssembly = value; - return _scriptAssembly; - }, _scriptAssembly, ControlAssemblySelector); - } - } - - /// - /// Gets or set the path to the Python file in use - /// - /// Optional: Can only be one of three Script, Python or Xslt - [DataMember] - public string ScriptPath - { - get { return _python; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _python = value; - return _python; - }, _python, ScriptPathSelector); - } - } - - /// - /// Gets or sets the path to the Xslt file in use - /// - /// Optional: Can only be one of three Script, Python or Xslt - [DataMember] - public string XsltPath - { - get { return _xslt; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _xslt = value; - return _xslt; - }, _xslt, XsltPathSelector); - } - } - - /// - /// Gets or sets a list of Macro Properties - /// - [DataMember] - public MacroPropertyCollection Properties - { - get { return _properties; } - } - - - } +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; +using System.Text.RegularExpressions; +using Umbraco.Core.IO; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models +{ + /// + /// Represents a Macro + /// + [Serializable] + [DataContract(IsReference = true)] + internal class Macro : Entity, IMacro + { + public Macro() + { + _properties = new MacroPropertyCollection(); + _properties.CollectionChanged += PropertiesChanged; + _addedProperties = new List(); + _removedProperties = new List(); + } + + /// + /// Creates an item with pre-filled properties + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Macro(int id, bool useInEditor, int cacheDuration, string @alias, string name, string controlType, string controlAssembly, string xsltPath, bool cacheByPage, bool cacheByMember, bool dontRender, string scriptPath) + : this() + { + Id = id; + UseInEditor = useInEditor; + CacheDuration = cacheDuration; + Alias = alias; + Name = name; + ControlType = controlType; + ControlAssembly = controlAssembly; + XsltPath = xsltPath; + CacheByPage = cacheByPage; + CacheByMember = cacheByMember; + DontRender = dontRender; + ScriptPath = scriptPath; + } + + /// + /// Creates an instance for persisting a new item + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Macro(string @alias, string name, + string controlType = "", + string controlAssembly = "", + string xsltPath = "", + string scriptPath = "", + bool cacheByPage = false, + bool cacheByMember = false, + bool dontRender = true, + bool useInEditor = false, + int cacheDuration = 0) + : this() + { + UseInEditor = useInEditor; + CacheDuration = cacheDuration; + Alias = alias; + Name = name; + ControlType = controlType; + ControlAssembly = controlAssembly; + XsltPath = xsltPath; + CacheByPage = cacheByPage; + CacheByMember = cacheByMember; + DontRender = dontRender; + ScriptPath = scriptPath; + } + + private string _alias; + private string _name; + private bool _useInEditor; + private int _cacheDuration; + private bool _cacheByPage; + private bool _cacheByMember; + private bool _dontRender; + private string _scriptFile; + private string _scriptAssembly; + private string _python; + private string _xslt; + private readonly MacroPropertyCollection _properties; + private readonly List _addedProperties; + private readonly List _removedProperties; + + private static readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo(x => x.Alias); + private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); + private static readonly PropertyInfo UseInEditorSelector = ExpressionHelper.GetPropertyInfo(x => x.UseInEditor); + private static readonly PropertyInfo CacheDurationSelector = ExpressionHelper.GetPropertyInfo(x => x.CacheDuration); + private static readonly PropertyInfo CacheByPageSelector = ExpressionHelper.GetPropertyInfo(x => x.CacheByPage); + private static readonly PropertyInfo CacheByMemberSelector = ExpressionHelper.GetPropertyInfo(x => x.CacheByMember); + private static readonly PropertyInfo DontRenderSelector = ExpressionHelper.GetPropertyInfo(x => x.DontRender); + private static readonly PropertyInfo ControlPathSelector = ExpressionHelper.GetPropertyInfo(x => x.ControlType); + private static readonly PropertyInfo ControlAssemblySelector = ExpressionHelper.GetPropertyInfo(x => x.ControlAssembly); + private static readonly PropertyInfo ScriptPathSelector = ExpressionHelper.GetPropertyInfo(x => x.ScriptPath); + private static readonly PropertyInfo XsltPathSelector = ExpressionHelper.GetPropertyInfo(x => x.XsltPath); + private static readonly PropertyInfo PropertiesSelector = ExpressionHelper.GetPropertyInfo(x => x.Properties); + + void PropertiesChanged(object sender, NotifyCollectionChangedEventArgs e) + { + OnPropertyChanged(PropertiesSelector); + + if (e.Action == NotifyCollectionChangedAction.Add) + { + //listen for changes + var prop = e.NewItems.Cast().First(); + prop.PropertyChanged += PropertyDataChanged; + + var alias = prop.Alias; + + //remove from the removed/added props (since people could add/remove all they want in one request) + _removedProperties.RemoveAll(s => s == alias); + _addedProperties.RemoveAll(s => s == alias); + + //add to the added props + _addedProperties.Add(alias); + } + else if (e.Action == NotifyCollectionChangedAction.Remove) + { + //remove listening for changes + var prop = e.OldItems.Cast().First(); + prop.PropertyChanged -= PropertyDataChanged; + + var alias = prop.Alias; + + //remove from the removed/added props (since people could add/remove all they want in one request) + _removedProperties.RemoveAll(s => s == alias); + _addedProperties.RemoveAll(s => s == alias); + + //add to the added props + _removedProperties.Add(alias); + } + } + + /// + /// When some data of a property has changed ensure our Properties flag is dirty + /// + /// + /// + void PropertyDataChanged(object sender, PropertyChangedEventArgs e) + { + OnPropertyChanged(PropertiesSelector); + } + + public override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties) + { + _addedProperties.Clear(); + _removedProperties.Clear(); + base.ResetDirtyProperties(rememberPreviouslyChangedProperties); + foreach (var prop in Properties) + { + ((TracksChangesEntityBase)prop).ResetDirtyProperties(rememberPreviouslyChangedProperties); + } + } + + /// + /// Used internally to check if we need to add a section in the repository to the db + /// + internal IEnumerable AddedProperties + { + get { return _addedProperties; } + } + + /// + /// Used internally to check if we need to remove a section in the repository to the db + /// + internal IEnumerable RemovedProperties + { + get { return _removedProperties; } + } + + /// + /// Gets or sets the alias of the Macro + /// + [DataMember] + public string Alias + { + get { return _alias; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _alias = value; + return _alias; + }, _alias, AliasSelector); + } + } + + /// + /// Gets or sets the name of the Macro + /// + [DataMember] + public string Name + { + get { return _name; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _name = value; + return _name; + }, _name, NameSelector); + } + } + + /// + /// Gets or sets a boolean indicating whether the Macro can be used in an Editor + /// + [DataMember] + public bool UseInEditor + { + get { return _useInEditor; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _useInEditor = value; + return _useInEditor; + }, _useInEditor, UseInEditorSelector); + } + } + + /// + /// Gets or sets the Cache Duration for the Macro + /// + [DataMember] + public int CacheDuration + { + get { return _cacheDuration; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _cacheDuration = value; + return _cacheDuration; + }, _cacheDuration, CacheDurationSelector); + } + } + + /// + /// Gets or sets a boolean indicating whether the Macro should be Cached by Page + /// + [DataMember] + public bool CacheByPage + { + get { return _cacheByPage; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _cacheByPage = value; + return _cacheByPage; + }, _cacheByPage, CacheByPageSelector); + } + } + + /// + /// Gets or sets a boolean indicating whether the Macro should be Cached Personally + /// + [DataMember] + public bool CacheByMember + { + get { return _cacheByMember; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _cacheByMember = value; + return _cacheByMember; + }, _cacheByMember, CacheByMemberSelector); + } + } + + /// + /// Gets or sets a boolean indicating whether the Macro should be rendered in an Editor + /// + [DataMember] + public bool DontRender + { + get { return _dontRender; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _dontRender = value; + return _dontRender; + }, _dontRender, DontRenderSelector); + } + } + + /// + /// Gets or sets the path to user control or the Control Type to render + /// + [DataMember] + public string ControlType + { + get { return _scriptFile; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _scriptFile = value; + return _scriptFile; + }, _scriptFile, ControlPathSelector); + } + } + + /// + /// Gets or sets the name of the assembly, which should be used by the Macro + /// + /// Will usually only be filled if the ControlType is a Usercontrol + [DataMember] + public string ControlAssembly + { + get { return _scriptAssembly; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _scriptAssembly = value; + return _scriptAssembly; + }, _scriptAssembly, ControlAssemblySelector); + } + } + + /// + /// Gets or set the path to the Python file in use + /// + /// Optional: Can only be one of three Script, Python or Xslt + [DataMember] + public string ScriptPath + { + get { return _python; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _python = value; + return _python; + }, _python, ScriptPathSelector); + } + } + + /// + /// Gets or sets the path to the Xslt file in use + /// + /// Optional: Can only be one of three Script, Python or Xslt + [DataMember] + public string XsltPath + { + get { return _xslt; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _xslt = value; + return _xslt; + }, _xslt, XsltPathSelector); + } + } + + /// + /// Gets or sets a list of Macro Properties + /// + [DataMember] + public MacroPropertyCollection Properties + { + get { return _properties; } + } + + + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/MacroProperty.cs b/src/Umbraco.Core/Models/MacroProperty.cs index f44de98eb6..199b686dc9 100644 --- a/src/Umbraco.Core/Models/MacroProperty.cs +++ b/src/Umbraco.Core/Models/MacroProperty.cs @@ -1,181 +1,181 @@ -using System; -using System.Reflection; -using System.Runtime.Serialization; -using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.PropertyEditors; - -namespace Umbraco.Core.Models -{ - /// - /// Represents a Macro Property - /// - [Serializable] - [DataContract(IsReference = true)] - internal class MacroProperty : TracksChangesEntityBase, IMacroProperty, IRememberBeingDirty - { - public MacroProperty() - { - - } - - /// - /// Ctor for creating a new property - /// - /// - /// - /// - /// - public MacroProperty(string @alias, string name, int sortOrder, string editorAlias) - { - _alias = alias; - _name = name; - _sortOrder = sortOrder; - - //try to get the new mapped parameter editor - var mapped = LegacyParameterEditorAliasConverter.GetNewAliasFromLegacyAlias(editorAlias, false); - if (mapped.IsNullOrWhiteSpace() == false) - { - editorAlias = mapped; - } - - _editorAlias = editorAlias; - } - - /// - /// Ctor for creating an existing property - /// - /// - /// - /// - /// - /// - internal MacroProperty(int id, string @alias, string name, int sortOrder, string editorAlias) - { - _id = id; - _alias = alias; - _name = name; - _sortOrder = sortOrder; - - //try to get the new mapped parameter editor - var mapped = LegacyParameterEditorAliasConverter.GetNewAliasFromLegacyAlias(editorAlias, false); - if (mapped.IsNullOrWhiteSpace() == false) - { - editorAlias = mapped; - } - - _editorAlias = editorAlias; - } - - private string _alias; - private string _name; - private int _sortOrder; - private int _id; - private string _editorAlias; - - private static readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo(x => x.Alias); - private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); - private static readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo(x => x.SortOrder); - private static readonly PropertyInfo IdSelector = ExpressionHelper.GetPropertyInfo(x => x.Id); - private static readonly PropertyInfo PropertyTypeSelector = ExpressionHelper.GetPropertyInfo(x => x.EditorAlias); - - /// - /// Gets or sets the Alias of the Property - /// - [DataMember] - public int Id - { - get { return _id; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _id = value; - return _alias; - }, _alias, IdSelector); - } - } - - /// - /// Gets or sets the Alias of the Property - /// - [DataMember] - public string Alias - { - get { return _alias; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _alias = value; - return _alias; - }, _alias, AliasSelector); - } - } - - /// - /// Gets or sets the Name of the Property - /// - [DataMember] - public string Name - { - get { return _name; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _name = value; - return _name; - }, _name, NameSelector); - } - } - - /// - /// Gets or sets the Sort Order of the Property - /// - [DataMember] - public int SortOrder - { - get { return _sortOrder; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _sortOrder = value; - return _sortOrder; - }, _sortOrder, SortOrderSelector); - } - } - - /// - /// Gets or sets the Type for this Property - /// - /// - /// The MacroPropertyTypes acts as a plugin for Macros. - /// All types was previously contained in the database, but has been ported to code. - /// - [DataMember] - public string EditorAlias - - { - get { return _editorAlias; } - set - { - SetPropertyValueAndDetectChanges(o => - { - //try to get the new mapped parameter editor - var mapped = LegacyParameterEditorAliasConverter.GetNewAliasFromLegacyAlias(value, false); - if (mapped.IsNullOrWhiteSpace() == false) - { - _editorAlias = mapped; - } - else - { - _editorAlias = value; - } - - return _editorAlias; - }, _editorAlias, PropertyTypeSelector); - } - } - } +using System; +using System.Reflection; +using System.Runtime.Serialization; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Core.Models +{ + /// + /// Represents a Macro Property + /// + [Serializable] + [DataContract(IsReference = true)] + internal class MacroProperty : TracksChangesEntityBase, IMacroProperty, IRememberBeingDirty + { + public MacroProperty() + { + + } + + /// + /// Ctor for creating a new property + /// + /// + /// + /// + /// + public MacroProperty(string @alias, string name, int sortOrder, string editorAlias) + { + _alias = alias; + _name = name; + _sortOrder = sortOrder; + + //try to get the new mapped parameter editor + var mapped = LegacyParameterEditorAliasConverter.GetNewAliasFromLegacyAlias(editorAlias, false); + if (mapped.IsNullOrWhiteSpace() == false) + { + editorAlias = mapped; + } + + _editorAlias = editorAlias; + } + + /// + /// Ctor for creating an existing property + /// + /// + /// + /// + /// + /// + internal MacroProperty(int id, string @alias, string name, int sortOrder, string editorAlias) + { + _id = id; + _alias = alias; + _name = name; + _sortOrder = sortOrder; + + //try to get the new mapped parameter editor + var mapped = LegacyParameterEditorAliasConverter.GetNewAliasFromLegacyAlias(editorAlias, false); + if (mapped.IsNullOrWhiteSpace() == false) + { + editorAlias = mapped; + } + + _editorAlias = editorAlias; + } + + private string _alias; + private string _name; + private int _sortOrder; + private int _id; + private string _editorAlias; + + private static readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo(x => x.Alias); + private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); + private static readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo(x => x.SortOrder); + private static readonly PropertyInfo IdSelector = ExpressionHelper.GetPropertyInfo(x => x.Id); + private static readonly PropertyInfo PropertyTypeSelector = ExpressionHelper.GetPropertyInfo(x => x.EditorAlias); + + /// + /// Gets or sets the Alias of the Property + /// + [DataMember] + public int Id + { + get { return _id; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _id = value; + return _alias; + }, _alias, IdSelector); + } + } + + /// + /// Gets or sets the Alias of the Property + /// + [DataMember] + public string Alias + { + get { return _alias; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _alias = value; + return _alias; + }, _alias, AliasSelector); + } + } + + /// + /// Gets or sets the Name of the Property + /// + [DataMember] + public string Name + { + get { return _name; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _name = value; + return _name; + }, _name, NameSelector); + } + } + + /// + /// Gets or sets the Sort Order of the Property + /// + [DataMember] + public int SortOrder + { + get { return _sortOrder; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _sortOrder = value; + return _sortOrder; + }, _sortOrder, SortOrderSelector); + } + } + + /// + /// Gets or sets the Type for this Property + /// + /// + /// The MacroPropertyTypes acts as a plugin for Macros. + /// All types was previously contained in the database, but has been ported to code. + /// + [DataMember] + public string EditorAlias + + { + get { return _editorAlias; } + set + { + SetPropertyValueAndDetectChanges(o => + { + //try to get the new mapped parameter editor + var mapped = LegacyParameterEditorAliasConverter.GetNewAliasFromLegacyAlias(value, false); + if (mapped.IsNullOrWhiteSpace() == false) + { + _editorAlias = mapped; + } + else + { + _editorAlias = value; + } + + return _editorAlias; + }, _editorAlias, PropertyTypeSelector); + } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/MacroPropertyCollection.cs b/src/Umbraco.Core/Models/MacroPropertyCollection.cs index c8c466c368..6d0c762154 100644 --- a/src/Umbraco.Core/Models/MacroPropertyCollection.cs +++ b/src/Umbraco.Core/Models/MacroPropertyCollection.cs @@ -1,20 +1,20 @@ -using System; -using System.Collections.ObjectModel; -using System.Collections.Generic; -using System.Linq; - -namespace Umbraco.Core.Models -{ - /// - /// A macro's property collection - /// - public class MacroPropertyCollection : ObservableDictionary - { - public MacroPropertyCollection() - : base(property => property.Alias) - { - } - - } - +using System; +using System.Collections.ObjectModel; +using System.Collections.Generic; +using System.Linq; + +namespace Umbraco.Core.Models +{ + /// + /// A macro's property collection + /// + public class MacroPropertyCollection : ObservableDictionary + { + public MacroPropertyCollection() + : base(property => property.Alias) + { + } + + } + } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/MacroPropertyTypeBaseTypes.cs b/src/Umbraco.Core/Models/MacroPropertyTypeBaseTypes.cs index a89946b9a4..f997e90b57 100644 --- a/src/Umbraco.Core/Models/MacroPropertyTypeBaseTypes.cs +++ b/src/Umbraco.Core/Models/MacroPropertyTypeBaseTypes.cs @@ -1,20 +1,20 @@ -using System; -using System.Runtime.Serialization; - -namespace Umbraco.Core.Models -{ - /// - /// Enum for the three allowed BaseTypes - /// - [Serializable] - [DataContract(IsReference = true)] - internal enum MacroPropertyTypeBaseTypes - { - [EnumMember] - Int32, - [EnumMember] - Boolean, - [EnumMember] - String - } +using System; +using System.Runtime.Serialization; + +namespace Umbraco.Core.Models +{ + /// + /// Enum for the three allowed BaseTypes + /// + [Serializable] + [DataContract(IsReference = true)] + internal enum MacroPropertyTypeBaseTypes + { + [EnumMember] + Int32, + [EnumMember] + Boolean, + [EnumMember] + String + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/MacroTypes.cs b/src/Umbraco.Core/Models/MacroTypes.cs index 14f67cccfb..caf9586362 100644 --- a/src/Umbraco.Core/Models/MacroTypes.cs +++ b/src/Umbraco.Core/Models/MacroTypes.cs @@ -1,28 +1,28 @@ -using System; -using System.Runtime.Serialization; - -namespace Umbraco.Core.Models -{ - /// - /// Enum for the various types of Macros - /// - [Serializable] - [DataContract(IsReference = true)] - internal enum MacroTypes - { - [EnumMember] - Xslt = 1, - [EnumMember] - CustomControl = 2, - [EnumMember] - UserControl = 3, - [EnumMember] - Unknown = 4, - [EnumMember] - Python = 5, - [EnumMember] - Script = 6, - [EnumMember] - PartialView = 7 - } +using System; +using System.Runtime.Serialization; + +namespace Umbraco.Core.Models +{ + /// + /// Enum for the various types of Macros + /// + [Serializable] + [DataContract(IsReference = true)] + internal enum MacroTypes + { + [EnumMember] + Xslt = 1, + [EnumMember] + CustomControl = 2, + [EnumMember] + UserControl = 3, + [EnumMember] + Unknown = 4, + [EnumMember] + Python = 5, + [EnumMember] + Script = 6, + [EnumMember] + PartialView = 7 + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Member.cs b/src/Umbraco.Core/Models/Member.cs index 3530e94c59..f09b3a126c 100644 --- a/src/Umbraco.Core/Models/Member.cs +++ b/src/Umbraco.Core/Models/Member.cs @@ -1,454 +1,454 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Runtime.Serialization; - -namespace Umbraco.Core.Models -{ - /// - /// Represents a Member object - /// - [Serializable] - [DataContract(IsReference = true)] - public class Member : ContentBase, IMember - { - private readonly IMemberType _contentType; - private string _contentTypeAlias; - private string _username; - private string _email; - private string _password; - private object _providerUserKey; - private Type _userTypeKey; - - /// - /// Constructor for creating a Member object - /// - /// Name of the content - /// ContentType for the current Content object - public Member(string name, IMemberType contentType) - : base(name, -1, contentType, new PropertyCollection()) - { - _contentType = contentType; - } - - public Member(string name, string email, string username, string password, int parentId, IMemberType contentType) - : base(name, parentId, contentType, new PropertyCollection()) - { - Mandate.ParameterNotNull(contentType, "contentType"); - - _contentType = contentType; - _email = email; - _username = username; - _password = password; - } - - public Member(string name, string email, string username, string password, IContentBase parent, IMemberType contentType) - : base(name, parent, contentType, new PropertyCollection()) - { - Mandate.ParameterNotNull(contentType, "contentType"); - - _contentType = contentType; - _email = email; - _username = username; - _password = password; - } - - private static readonly PropertyInfo DefaultContentTypeAliasSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeAlias); - private static readonly PropertyInfo UsernameSelector = ExpressionHelper.GetPropertyInfo(x => x.Username); - private static readonly PropertyInfo EmailSelector = ExpressionHelper.GetPropertyInfo(x => x.Email); - private static readonly PropertyInfo PasswordSelector = ExpressionHelper.GetPropertyInfo(x => x.Password); - private static readonly PropertyInfo ProviderUserKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ProviderUserKey); - private static readonly PropertyInfo UserTypeKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ProviderUserKeyType); - - /// - /// Gets or sets the Username - /// - [DataMember] - public string Username - { - get { return _username; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _username = value; - return _username; - }, _username, UsernameSelector); - } - } - - /// - /// Gets or sets the Email - /// - [DataMember] - public string Email - { - get { return _email; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _email = value; - return _email; - }, _email, EmailSelector); - } - } - - /// - /// Gets or sets the Password - /// - [DataMember] - public string Password - { - get { return _password; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _password = value; - return _password; - }, _password, PasswordSelector); - } - } - - /// - /// Gets or sets the Groups that Member is part of - /// - [DataMember] - public IEnumerable Groups { get; set; } - - /// - /// Gets or sets the Password Question - /// - /// - /// Alias: umbracoPasswordRetrievalQuestionPropertyTypeAlias - /// Part of the standard properties collection. - /// - [IgnoreDataMember] - public string PasswordQuestion - { - get - { - return Properties[Constants.Conventions.Member.PasswordQuestion].Value == null - ? string.Empty - : Properties[Constants.Conventions.Member.PasswordQuestion].Value.ToString(); - } - set - { - Properties[Constants.Conventions.Member.PasswordQuestion].Value = value; - } - } - - /// - /// Gets or sets the Password Answer - /// - /// - /// Alias: umbracoPasswordRetrievalAnswerPropertyTypeAlias - /// Part of the standard properties collection. - /// - [IgnoreDataMember] - public string PasswordAnswer - { - get - { - return Properties[Constants.Conventions.Member.PasswordAnswer].Value == null - ? string.Empty - : Properties[Constants.Conventions.Member.PasswordAnswer].Value.ToString(); - } - set - { - Properties[Constants.Conventions.Member.PasswordAnswer].Value = value; - } - } - - /// - /// Gets or set the comments for the member - /// - /// - /// Alias: umbracoCommentPropertyTypeAlias - /// Part of the standard properties collection. - /// - [IgnoreDataMember] - public string Comments - { - get - { - return Properties[Constants.Conventions.Member.Comments].Value == null - ? string.Empty - : Properties[Constants.Conventions.Member.Comments].Value.ToString(); - } - set - { - Properties[Constants.Conventions.Member.Comments].Value = value; - } - } - - /// - /// Gets or sets a boolean indicating whether the Member is approved - /// - /// - /// Alias: umbracoApprovePropertyTypeAlias - /// Part of the standard properties collection. - /// - [IgnoreDataMember] - public bool IsApproved - { - get - { - if (Properties[Constants.Conventions.Member.IsApproved].Value == null) - return default(bool); - - if (Properties[Constants.Conventions.Member.IsApproved].Value is bool) - return (bool)Properties[Constants.Conventions.Member.IsApproved].Value; - - return (bool)Convert.ChangeType(Properties[Constants.Conventions.Member.IsApproved].Value, typeof(bool)); - } - set - { - Properties[Constants.Conventions.Member.IsApproved].Value = value; - } - } - - /// - /// Gets or sets a boolean indicating whether the Member is locked out - /// - /// - /// Alias: umbracoLockPropertyTypeAlias - /// Part of the standard properties collection. - /// - [IgnoreDataMember] - public bool IsLockedOut - { - get - { - if (Properties[Constants.Conventions.Member.IsLockedOut].Value == null) - return default(bool); - - if (Properties[Constants.Conventions.Member.IsLockedOut].Value is bool) - return (bool)Properties[Constants.Conventions.Member.IsLockedOut].Value; - - return (bool)Convert.ChangeType(Properties[Constants.Conventions.Member.IsLockedOut].Value, typeof(bool)); - } - set - { - Properties[Constants.Conventions.Member.IsLockedOut].Value = value; - } - } - - /// - /// Gets or sets the date for last login - /// - /// - /// Alias: umbracoLastLoginPropertyTypeAlias - /// Part of the standard properties collection. - /// - [IgnoreDataMember] - public DateTime LastLoginDate - { - get - { - if (Properties[Constants.Conventions.Member.LastLoginDate].Value == null) - return default(DateTime); - - if (Properties[Constants.Conventions.Member.LastLoginDate].Value is DateTime) - return (DateTime)Properties[Constants.Conventions.Member.LastLoginDate].Value; - - return (DateTime)Convert.ChangeType(Properties[Constants.Conventions.Member.LastLoginDate].Value, typeof(DateTime)); - } - set - { - Properties[Constants.Conventions.Member.LastLoginDate].Value = value; - } - } - - /// - /// Gest or sets the date for last password change - /// - /// - /// Alias: umbracoMemberLastPasswordChange - /// Part of the standard properties collection. - /// - [IgnoreDataMember] - public DateTime LastPasswordChangeDate - { - get - { - if (Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value == null) - return default(DateTime); - - if (Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value is DateTime) - return (DateTime)Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value; - - return (DateTime)Convert.ChangeType(Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value, typeof(DateTime)); - } - set - { - Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value = value; - } - } - - /// - /// Gets or sets the date for when Member was locked out - /// - /// - /// Alias: umbracoMemberLastLockout - /// Part of the standard properties collection. - /// - [IgnoreDataMember] - public DateTime LastLockoutDate - { - get - { - if (Properties[Constants.Conventions.Member.LastLockoutDate].Value == null) - return default(DateTime); - - if (Properties[Constants.Conventions.Member.LastLockoutDate].Value is DateTime) - return (DateTime)Properties[Constants.Conventions.Member.LastLockoutDate].Value; - - return (DateTime)Convert.ChangeType(Properties[Constants.Conventions.Member.LastLockoutDate].Value, typeof(DateTime)); - } - set - { - Properties[Constants.Conventions.Member.LastLockoutDate].Value = value; - } - } - - /// - /// Gets or sets the number of failed password attempts. - /// This is the number of times the password was entered incorrectly upon login. - /// - /// - /// Alias: umbracoFailedPasswordAttemptsPropertyTypeAlias - /// Part of the standard properties collection. - /// - [IgnoreDataMember] - public int FailedPasswordAttempts - { - get - { - if (Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value == null) - return default(int); - - if (Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value is int) - return (int)Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value; - - return (int)Convert.ChangeType(Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value, typeof(int)); - } - set - { - Properties[Constants.Conventions.Member.LastLockoutDate].Value = value; - } - } - - /// - /// String alias of the default ContentType - /// - [DataMember] - public virtual string ContentTypeAlias - { - get { return _contentTypeAlias; } - internal set - { - SetPropertyValueAndDetectChanges(o => - { - _contentTypeAlias = value; - return _contentTypeAlias; - }, _contentTypeAlias, DefaultContentTypeAliasSelector); - } - } - - /// - /// User key from the Provider. - /// - /// - /// When using standard umbraco provider this key will - /// correspond to the guid UniqueId/Key. - /// Otherwise it will the one available from the asp.net - /// membership provider. - /// - [DataMember] - internal virtual object ProviderUserKey - { - get - { - return _providerUserKey; - } - set - { - SetPropertyValueAndDetectChanges(o => - { - _providerUserKey = value; - return _providerUserKey; - }, _providerUserKey, ProviderUserKeySelector); - } - } - - /// - /// Gets or sets the type of the provider user key. - /// - /// - /// The type of the provider user key. - /// - [IgnoreDataMember] - internal Type ProviderUserKeyType - { - get - { - return _userTypeKey; - } - private set - { - SetPropertyValueAndDetectChanges(o => - { - _userTypeKey = value; - return _userTypeKey; - }, _userTypeKey, UserTypeKeySelector); - } - } - - /// - /// Sets the type of the provider user key. - /// - /// The type. - internal void SetProviderUserKeyType(Type type) - { - ProviderUserKeyType = type; - } - - /// - /// Method to call when Entity is being saved - /// - /// Created date is set and a Unique key is assigned - internal override void AddingEntity() - { - base.AddingEntity(); - - if (Key == Guid.Empty) - Key = Guid.NewGuid(); - } - - /// - /// Gets the ContentType used by this content object - /// - [IgnoreDataMember] - public IMemberType ContentType - { - get { return _contentType; } - } - - public override void ChangeTrashedState(bool isTrashed, int parentId = -20) - { - throw new NotSupportedException("Members can't be trashed as no Recycle Bin exists, so use of this method is invalid"); - } - - /* Internal experiment - only used for mapping queries. - * Adding these to have first level properties instead of the Properties collection. - */ - internal string LongStringPropertyValue { get; set; } - internal string ShortStringPropertyValue { get; set; } - internal int IntegerropertyValue { get; set; } - internal bool BoolPropertyValue { get; set; } - internal DateTime DateTimePropertyValue { get; set; } - internal string PropertyTypeAlias { get; set; } - } +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.Serialization; + +namespace Umbraco.Core.Models +{ + /// + /// Represents a Member object + /// + [Serializable] + [DataContract(IsReference = true)] + public class Member : ContentBase, IMember + { + private readonly IMemberType _contentType; + private string _contentTypeAlias; + private string _username; + private string _email; + private string _password; + private object _providerUserKey; + private Type _userTypeKey; + + /// + /// Constructor for creating a Member object + /// + /// Name of the content + /// ContentType for the current Content object + public Member(string name, IMemberType contentType) + : base(name, -1, contentType, new PropertyCollection()) + { + _contentType = contentType; + } + + public Member(string name, string email, string username, string password, int parentId, IMemberType contentType) + : base(name, parentId, contentType, new PropertyCollection()) + { + Mandate.ParameterNotNull(contentType, "contentType"); + + _contentType = contentType; + _email = email; + _username = username; + _password = password; + } + + public Member(string name, string email, string username, string password, IContentBase parent, IMemberType contentType) + : base(name, parent, contentType, new PropertyCollection()) + { + Mandate.ParameterNotNull(contentType, "contentType"); + + _contentType = contentType; + _email = email; + _username = username; + _password = password; + } + + private static readonly PropertyInfo DefaultContentTypeAliasSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeAlias); + private static readonly PropertyInfo UsernameSelector = ExpressionHelper.GetPropertyInfo(x => x.Username); + private static readonly PropertyInfo EmailSelector = ExpressionHelper.GetPropertyInfo(x => x.Email); + private static readonly PropertyInfo PasswordSelector = ExpressionHelper.GetPropertyInfo(x => x.Password); + private static readonly PropertyInfo ProviderUserKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ProviderUserKey); + private static readonly PropertyInfo UserTypeKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ProviderUserKeyType); + + /// + /// Gets or sets the Username + /// + [DataMember] + public string Username + { + get { return _username; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _username = value; + return _username; + }, _username, UsernameSelector); + } + } + + /// + /// Gets or sets the Email + /// + [DataMember] + public string Email + { + get { return _email; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _email = value; + return _email; + }, _email, EmailSelector); + } + } + + /// + /// Gets or sets the Password + /// + [DataMember] + public string Password + { + get { return _password; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _password = value; + return _password; + }, _password, PasswordSelector); + } + } + + /// + /// Gets or sets the Groups that Member is part of + /// + [DataMember] + public IEnumerable Groups { get; set; } + + /// + /// Gets or sets the Password Question + /// + /// + /// Alias: umbracoPasswordRetrievalQuestionPropertyTypeAlias + /// Part of the standard properties collection. + /// + [IgnoreDataMember] + public string PasswordQuestion + { + get + { + return Properties[Constants.Conventions.Member.PasswordQuestion].Value == null + ? string.Empty + : Properties[Constants.Conventions.Member.PasswordQuestion].Value.ToString(); + } + set + { + Properties[Constants.Conventions.Member.PasswordQuestion].Value = value; + } + } + + /// + /// Gets or sets the Password Answer + /// + /// + /// Alias: umbracoPasswordRetrievalAnswerPropertyTypeAlias + /// Part of the standard properties collection. + /// + [IgnoreDataMember] + public string PasswordAnswer + { + get + { + return Properties[Constants.Conventions.Member.PasswordAnswer].Value == null + ? string.Empty + : Properties[Constants.Conventions.Member.PasswordAnswer].Value.ToString(); + } + set + { + Properties[Constants.Conventions.Member.PasswordAnswer].Value = value; + } + } + + /// + /// Gets or set the comments for the member + /// + /// + /// Alias: umbracoCommentPropertyTypeAlias + /// Part of the standard properties collection. + /// + [IgnoreDataMember] + public string Comments + { + get + { + return Properties[Constants.Conventions.Member.Comments].Value == null + ? string.Empty + : Properties[Constants.Conventions.Member.Comments].Value.ToString(); + } + set + { + Properties[Constants.Conventions.Member.Comments].Value = value; + } + } + + /// + /// Gets or sets a boolean indicating whether the Member is approved + /// + /// + /// Alias: umbracoApprovePropertyTypeAlias + /// Part of the standard properties collection. + /// + [IgnoreDataMember] + public bool IsApproved + { + get + { + if (Properties[Constants.Conventions.Member.IsApproved].Value == null) + return default(bool); + + if (Properties[Constants.Conventions.Member.IsApproved].Value is bool) + return (bool)Properties[Constants.Conventions.Member.IsApproved].Value; + + return (bool)Convert.ChangeType(Properties[Constants.Conventions.Member.IsApproved].Value, typeof(bool)); + } + set + { + Properties[Constants.Conventions.Member.IsApproved].Value = value; + } + } + + /// + /// Gets or sets a boolean indicating whether the Member is locked out + /// + /// + /// Alias: umbracoLockPropertyTypeAlias + /// Part of the standard properties collection. + /// + [IgnoreDataMember] + public bool IsLockedOut + { + get + { + if (Properties[Constants.Conventions.Member.IsLockedOut].Value == null) + return default(bool); + + if (Properties[Constants.Conventions.Member.IsLockedOut].Value is bool) + return (bool)Properties[Constants.Conventions.Member.IsLockedOut].Value; + + return (bool)Convert.ChangeType(Properties[Constants.Conventions.Member.IsLockedOut].Value, typeof(bool)); + } + set + { + Properties[Constants.Conventions.Member.IsLockedOut].Value = value; + } + } + + /// + /// Gets or sets the date for last login + /// + /// + /// Alias: umbracoLastLoginPropertyTypeAlias + /// Part of the standard properties collection. + /// + [IgnoreDataMember] + public DateTime LastLoginDate + { + get + { + if (Properties[Constants.Conventions.Member.LastLoginDate].Value == null) + return default(DateTime); + + if (Properties[Constants.Conventions.Member.LastLoginDate].Value is DateTime) + return (DateTime)Properties[Constants.Conventions.Member.LastLoginDate].Value; + + return (DateTime)Convert.ChangeType(Properties[Constants.Conventions.Member.LastLoginDate].Value, typeof(DateTime)); + } + set + { + Properties[Constants.Conventions.Member.LastLoginDate].Value = value; + } + } + + /// + /// Gest or sets the date for last password change + /// + /// + /// Alias: umbracoMemberLastPasswordChange + /// Part of the standard properties collection. + /// + [IgnoreDataMember] + public DateTime LastPasswordChangeDate + { + get + { + if (Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value == null) + return default(DateTime); + + if (Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value is DateTime) + return (DateTime)Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value; + + return (DateTime)Convert.ChangeType(Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value, typeof(DateTime)); + } + set + { + Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value = value; + } + } + + /// + /// Gets or sets the date for when Member was locked out + /// + /// + /// Alias: umbracoMemberLastLockout + /// Part of the standard properties collection. + /// + [IgnoreDataMember] + public DateTime LastLockoutDate + { + get + { + if (Properties[Constants.Conventions.Member.LastLockoutDate].Value == null) + return default(DateTime); + + if (Properties[Constants.Conventions.Member.LastLockoutDate].Value is DateTime) + return (DateTime)Properties[Constants.Conventions.Member.LastLockoutDate].Value; + + return (DateTime)Convert.ChangeType(Properties[Constants.Conventions.Member.LastLockoutDate].Value, typeof(DateTime)); + } + set + { + Properties[Constants.Conventions.Member.LastLockoutDate].Value = value; + } + } + + /// + /// Gets or sets the number of failed password attempts. + /// This is the number of times the password was entered incorrectly upon login. + /// + /// + /// Alias: umbracoFailedPasswordAttemptsPropertyTypeAlias + /// Part of the standard properties collection. + /// + [IgnoreDataMember] + public int FailedPasswordAttempts + { + get + { + if (Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value == null) + return default(int); + + if (Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value is int) + return (int)Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value; + + return (int)Convert.ChangeType(Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value, typeof(int)); + } + set + { + Properties[Constants.Conventions.Member.LastLockoutDate].Value = value; + } + } + + /// + /// String alias of the default ContentType + /// + [DataMember] + public virtual string ContentTypeAlias + { + get { return _contentTypeAlias; } + internal set + { + SetPropertyValueAndDetectChanges(o => + { + _contentTypeAlias = value; + return _contentTypeAlias; + }, _contentTypeAlias, DefaultContentTypeAliasSelector); + } + } + + /// + /// User key from the Provider. + /// + /// + /// When using standard umbraco provider this key will + /// correspond to the guid UniqueId/Key. + /// Otherwise it will the one available from the asp.net + /// membership provider. + /// + [DataMember] + internal virtual object ProviderUserKey + { + get + { + return _providerUserKey; + } + set + { + SetPropertyValueAndDetectChanges(o => + { + _providerUserKey = value; + return _providerUserKey; + }, _providerUserKey, ProviderUserKeySelector); + } + } + + /// + /// Gets or sets the type of the provider user key. + /// + /// + /// The type of the provider user key. + /// + [IgnoreDataMember] + internal Type ProviderUserKeyType + { + get + { + return _userTypeKey; + } + private set + { + SetPropertyValueAndDetectChanges(o => + { + _userTypeKey = value; + return _userTypeKey; + }, _userTypeKey, UserTypeKeySelector); + } + } + + /// + /// Sets the type of the provider user key. + /// + /// The type. + internal void SetProviderUserKeyType(Type type) + { + ProviderUserKeyType = type; + } + + /// + /// Method to call when Entity is being saved + /// + /// Created date is set and a Unique key is assigned + internal override void AddingEntity() + { + base.AddingEntity(); + + if (Key == Guid.Empty) + Key = Guid.NewGuid(); + } + + /// + /// Gets the ContentType used by this content object + /// + [IgnoreDataMember] + public IMemberType ContentType + { + get { return _contentType; } + } + + public override void ChangeTrashedState(bool isTrashed, int parentId = -20) + { + throw new NotSupportedException("Members can't be trashed as no Recycle Bin exists, so use of this method is invalid"); + } + + /* Internal experiment - only used for mapping queries. + * Adding these to have first level properties instead of the Properties collection. + */ + internal string LongStringPropertyValue { get; set; } + internal string ShortStringPropertyValue { get; set; } + internal int IntegerropertyValue { get; set; } + internal bool BoolPropertyValue { get; set; } + internal DateTime DateTimePropertyValue { get; set; } + internal string PropertyTypeAlias { get; set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/MemberType.cs b/src/Umbraco.Core/Models/MemberType.cs index 2b32681392..c361891fa0 100644 --- a/src/Umbraco.Core/Models/MemberType.cs +++ b/src/Umbraco.Core/Models/MemberType.cs @@ -1,139 +1,139 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Runtime.Serialization; - -namespace Umbraco.Core.Models -{ - /// - /// Represents the content type that a object is based on - /// - [Serializable] - [DataContract(IsReference = true)] - public class MemberType : ContentTypeCompositionBase, IMemberType - { - //Dictionary is divided into string: PropertyTypeAlias, Tuple: MemberCanEdit, VisibleOnProfile, PropertyTypeId - private IDictionary> _memberTypePropertyTypes; - - public MemberType(int parentId) : base(parentId) - { - _memberTypePropertyTypes = new Dictionary>(); - } - - public MemberType(IContentTypeComposition parent) : base(parent) - { - _memberTypePropertyTypes = new Dictionary>(); - } - - private static readonly PropertyInfo MemberTypePropertyTypesSelector = ExpressionHelper.GetPropertyInfo>>(x => x.MemberTypePropertyTypes); - - /// - /// Gets or Sets a Dictionary of Tuples (MemberCanEdit, VisibleOnProfile, PropertyTypeId) by the PropertyTypes' alias. - /// - [DataMember] - internal IDictionary> MemberTypePropertyTypes - { - get { return _memberTypePropertyTypes; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _memberTypePropertyTypes = value; - return _memberTypePropertyTypes; - }, _memberTypePropertyTypes, MemberTypePropertyTypesSelector); - } - } - - /// - /// Gets a boolean indicating whether a Property is editable by the Member. - /// - /// PropertyType Alias of the Property to check - /// - public bool MemberCanEditProperty(string propertyTypeAlias) - { - if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias)) - { - return MemberTypePropertyTypes[propertyTypeAlias].Item1; - } - - return false; - } - - /// - /// Gets a boolean indicating whether a Property is visible on the Members profile. - /// - /// PropertyType Alias of the Property to check - /// - public bool MemberCanViewProperty(string propertyTypeAlias) - { - if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias)) - { - return MemberTypePropertyTypes[propertyTypeAlias].Item2; - } - - return false; - } - - /// - /// Sets a boolean indicating whether a Property is editable by the Member. - /// - /// PropertyType Alias of the Property to set - /// Boolean value, true or false - public void SetMemberCanEditProperty(string propertyTypeAlias, bool value) - { - if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias)) - { - var tuple = MemberTypePropertyTypes[propertyTypeAlias]; - MemberTypePropertyTypes[propertyTypeAlias] = new Tuple(value, tuple.Item2, tuple.Item3); - } - else - { - var propertyType = PropertyTypes.First(x => x.Alias.Equals(propertyTypeAlias)); - var tuple = new Tuple(value, false, propertyType.Id); - MemberTypePropertyTypes.Add(propertyTypeAlias, tuple); - } - } - - /// - /// Sets a boolean indicating whether a Property is visible on the Members profile. - /// - /// PropertyType Alias of the Property to set - /// Boolean value, true or false - public void SetMemberCanViewProperty(string propertyTypeAlias, bool value) - { - if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias)) - { - var tuple = MemberTypePropertyTypes[propertyTypeAlias]; - MemberTypePropertyTypes[propertyTypeAlias] = new Tuple(tuple.Item1, value, tuple.Item3); - } - else - { - var propertyType = PropertyTypes.First(x => x.Alias.Equals(propertyTypeAlias)); - var tuple = new Tuple(false, value, propertyType.Id); - MemberTypePropertyTypes.Add(propertyTypeAlias, tuple); - } - } - - /// - /// Method to call when Entity is being saved - /// - /// Created date is set and a Unique key is assigned - internal override void AddingEntity() - { - base.AddingEntity(); - - if (Key == Guid.Empty) - Key = Guid.NewGuid(); - } - - /// - /// Method to call when Entity is being updated - /// - /// Modified Date is set and a new Version guid is set - internal override void UpdatingEntity() - { - base.UpdatingEntity(); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; + +namespace Umbraco.Core.Models +{ + /// + /// Represents the content type that a object is based on + /// + [Serializable] + [DataContract(IsReference = true)] + public class MemberType : ContentTypeCompositionBase, IMemberType + { + //Dictionary is divided into string: PropertyTypeAlias, Tuple: MemberCanEdit, VisibleOnProfile, PropertyTypeId + private IDictionary> _memberTypePropertyTypes; + + public MemberType(int parentId) : base(parentId) + { + _memberTypePropertyTypes = new Dictionary>(); + } + + public MemberType(IContentTypeComposition parent) : base(parent) + { + _memberTypePropertyTypes = new Dictionary>(); + } + + private static readonly PropertyInfo MemberTypePropertyTypesSelector = ExpressionHelper.GetPropertyInfo>>(x => x.MemberTypePropertyTypes); + + /// + /// Gets or Sets a Dictionary of Tuples (MemberCanEdit, VisibleOnProfile, PropertyTypeId) by the PropertyTypes' alias. + /// + [DataMember] + internal IDictionary> MemberTypePropertyTypes + { + get { return _memberTypePropertyTypes; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _memberTypePropertyTypes = value; + return _memberTypePropertyTypes; + }, _memberTypePropertyTypes, MemberTypePropertyTypesSelector); + } + } + + /// + /// Gets a boolean indicating whether a Property is editable by the Member. + /// + /// PropertyType Alias of the Property to check + /// + public bool MemberCanEditProperty(string propertyTypeAlias) + { + if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias)) + { + return MemberTypePropertyTypes[propertyTypeAlias].Item1; + } + + return false; + } + + /// + /// Gets a boolean indicating whether a Property is visible on the Members profile. + /// + /// PropertyType Alias of the Property to check + /// + public bool MemberCanViewProperty(string propertyTypeAlias) + { + if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias)) + { + return MemberTypePropertyTypes[propertyTypeAlias].Item2; + } + + return false; + } + + /// + /// Sets a boolean indicating whether a Property is editable by the Member. + /// + /// PropertyType Alias of the Property to set + /// Boolean value, true or false + public void SetMemberCanEditProperty(string propertyTypeAlias, bool value) + { + if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias)) + { + var tuple = MemberTypePropertyTypes[propertyTypeAlias]; + MemberTypePropertyTypes[propertyTypeAlias] = new Tuple(value, tuple.Item2, tuple.Item3); + } + else + { + var propertyType = PropertyTypes.First(x => x.Alias.Equals(propertyTypeAlias)); + var tuple = new Tuple(value, false, propertyType.Id); + MemberTypePropertyTypes.Add(propertyTypeAlias, tuple); + } + } + + /// + /// Sets a boolean indicating whether a Property is visible on the Members profile. + /// + /// PropertyType Alias of the Property to set + /// Boolean value, true or false + public void SetMemberCanViewProperty(string propertyTypeAlias, bool value) + { + if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias)) + { + var tuple = MemberTypePropertyTypes[propertyTypeAlias]; + MemberTypePropertyTypes[propertyTypeAlias] = new Tuple(tuple.Item1, value, tuple.Item3); + } + else + { + var propertyType = PropertyTypes.First(x => x.Alias.Equals(propertyTypeAlias)); + var tuple = new Tuple(false, value, propertyType.Id); + MemberTypePropertyTypes.Add(propertyTypeAlias, tuple); + } + } + + /// + /// Method to call when Entity is being saved + /// + /// Created date is set and a Unique key is assigned + internal override void AddingEntity() + { + base.AddingEntity(); + + if (Key == Guid.Empty) + Key = Guid.NewGuid(); + } + + /// + /// Method to call when Entity is being updated + /// + /// Modified Date is set and a new Version guid is set + internal override void UpdatingEntity() + { + base.UpdatingEntity(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/MembershipExtensions.cs b/src/Umbraco.Core/Models/Membership/MembershipExtensions.cs index a986869371..e88e807318 100644 --- a/src/Umbraco.Core/Models/Membership/MembershipExtensions.cs +++ b/src/Umbraco.Core/Models/Membership/MembershipExtensions.cs @@ -1,25 +1,25 @@ -using System; -using System.Web.Security; - -namespace Umbraco.Core.Models.Membership -{ - internal static class MembershipExtensions - { - internal static MembershipUser AsConcreteMembershipUser(this IMember member) - { - var membershipMember = new UmbracoMembershipMember(member); - return membershipMember; - } - - internal static IMember AsIMember(this MembershipUser membershipMember) - { - var member = membershipMember as UmbracoMembershipMember; - if (member != null) - { - return member.Member; - } - - throw new NotImplementedException(); - } - } +using System; +using System.Web.Security; + +namespace Umbraco.Core.Models.Membership +{ + internal static class MembershipExtensions + { + internal static MembershipUser AsConcreteMembershipUser(this IMember member) + { + var membershipMember = new UmbracoMembershipMember(member); + return membershipMember; + } + + internal static IMember AsIMember(this MembershipUser membershipMember) + { + var member = membershipMember as UmbracoMembershipMember; + if (member != null) + { + return member.Member; + } + + throw new NotImplementedException(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs b/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs index d55cc77f66..4397f766c0 100644 --- a/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs +++ b/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs @@ -1,66 +1,66 @@ -using System.Web.Security; - -namespace Umbraco.Core.Models.Membership -{ - //TODO: THere's still a bunch of properties that don't exist in this use that need to be mapped somehow. - - internal class UmbracoMembershipMember : MembershipUser - { - private readonly IMember _member; - - public UmbracoMembershipMember(IMember member) - { - _member = member; - } - - internal IMember Member - { - get { return _member; } - } - - public override string Email - { - get { return _member.Email; } - set { _member.Email = value; } - } - - public override object ProviderUserKey - { - get { return _member.Key; } - } - - public override System.DateTime CreationDate - { - get { return _member.CreateDate; } - } - - public override string UserName - { - get { return _member.Username; } - } - - public override string Comment - { - get { return _member.Comments; } - set { _member.Comments = value; } - } - - public override bool IsApproved - { - get { return _member.IsApproved; } - set { _member.IsApproved = value; } - } - - public override bool IsLockedOut - { - get { return _member.IsLockedOut; } - } - - public override System.DateTime LastLoginDate - { - get { return _member.LastLoginDate; } - set { _member.LastLoginDate = value; } - } - - } +using System.Web.Security; + +namespace Umbraco.Core.Models.Membership +{ + //TODO: THere's still a bunch of properties that don't exist in this use that need to be mapped somehow. + + internal class UmbracoMembershipMember : MembershipUser + { + private readonly IMember _member; + + public UmbracoMembershipMember(IMember member) + { + _member = member; + } + + internal IMember Member + { + get { return _member; } + } + + public override string Email + { + get { return _member.Email; } + set { _member.Email = value; } + } + + public override object ProviderUserKey + { + get { return _member.Key; } + } + + public override System.DateTime CreationDate + { + get { return _member.CreateDate; } + } + + public override string UserName + { + get { return _member.Username; } + } + + public override string Comment + { + get { return _member.Comments; } + set { _member.Comments = value; } + } + + public override bool IsApproved + { + get { return _member.IsApproved; } + set { _member.IsApproved = value; } + } + + public override bool IsLockedOut + { + get { return _member.IsLockedOut; } + } + + public override System.DateTime LastLoginDate + { + get { return _member.LastLoginDate; } + set { _member.LastLoginDate = value; } + } + + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/PagedResult.cs b/src/Umbraco.Core/Models/PagedResult.cs index 2e35da9a96..8625993f90 100644 --- a/src/Umbraco.Core/Models/PagedResult.cs +++ b/src/Umbraco.Core/Models/PagedResult.cs @@ -1,63 +1,63 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; - -namespace Umbraco.Core.Models -{ - /// - /// Represents a paged result for a model collection - /// - /// - [DataContract(Name = "pagedCollection", Namespace = "")] - public class PagedResult - { - public PagedResult(long totalItems, long pageNumber, long pageSize) - { - TotalItems = totalItems; - PageNumber = pageNumber; - PageSize = pageSize; - - if (pageSize > 0) - { - TotalPages = (long) Math.Ceiling(totalItems/(Decimal) pageSize); - } - else - { - TotalPages = 1; - } - } - - [DataMember(Name = "pageNumber")] - public long PageNumber { get; private set; } - - [DataMember(Name = "pageSize")] - public long PageSize { get; private set; } - - [DataMember(Name = "totalPages")] - public long TotalPages { get; private set; } - - [DataMember(Name = "totalItems")] - public long TotalItems { get; private set; } - - [DataMember(Name = "items")] - public IEnumerable Items { get; set; } - - /// - /// Calculates the skip size based on the paged parameters specified - /// - /// - /// Returns 0 if the page number or page size is zero - /// - internal int SkipSize - { - get - { - if (PageNumber > 0 && PageSize > 0) - { - return Convert.ToInt32((PageNumber - 1)*PageSize); - } - return 0; - } - } - } +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace Umbraco.Core.Models +{ + /// + /// Represents a paged result for a model collection + /// + /// + [DataContract(Name = "pagedCollection", Namespace = "")] + public class PagedResult + { + public PagedResult(long totalItems, long pageNumber, long pageSize) + { + TotalItems = totalItems; + PageNumber = pageNumber; + PageSize = pageSize; + + if (pageSize > 0) + { + TotalPages = (long) Math.Ceiling(totalItems/(Decimal) pageSize); + } + else + { + TotalPages = 1; + } + } + + [DataMember(Name = "pageNumber")] + public long PageNumber { get; private set; } + + [DataMember(Name = "pageSize")] + public long PageSize { get; private set; } + + [DataMember(Name = "totalPages")] + public long TotalPages { get; private set; } + + [DataMember(Name = "totalItems")] + public long TotalItems { get; private set; } + + [DataMember(Name = "items")] + public IEnumerable Items { get; set; } + + /// + /// Calculates the skip size based on the paged parameters specified + /// + /// + /// Returns 0 if the page number or page size is zero + /// + internal int SkipSize + { + get + { + if (PageNumber > 0 && PageSize > 0) + { + return Convert.ToInt32((PageNumber - 1)*PageSize); + } + return 0; + } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/PropertyTags.cs b/src/Umbraco.Core/Models/PropertyTags.cs index 70b0e2ad2d..d64a1c4edf 100644 --- a/src/Umbraco.Core/Models/PropertyTags.cs +++ b/src/Umbraco.Core/Models/PropertyTags.cs @@ -1,43 +1,43 @@ -using System; -using System.Collections.Generic; - -namespace Umbraco.Core.Models -{ - /// - /// A property extension class that allows us to enable tags for any given property - /// - internal class PropertyTags - { - public PropertyTags() - { - Enable = false; - Behavior = PropertyTagBehavior.Merge; - } - - /// - /// The behavior of how to save the tags assigned - - /// Merge (keep existing and append new), - /// Remove (remove any of the tags in the Tags property that are currently assigned, - /// Replace (replace the currently assigned tags with the ones specified) - /// - public PropertyTagBehavior Behavior { get; set; } - - /// - /// Flags the property to have tagging enabled - /// - public bool Enable { get; set; } - - /// - /// The actual tags to associate - tag/group - /// - public IEnumerable> Tags { get; set; } - - } - - internal enum PropertyTagBehavior - { - Replace, - Remove, - Merge - } +using System; +using System.Collections.Generic; + +namespace Umbraco.Core.Models +{ + /// + /// A property extension class that allows us to enable tags for any given property + /// + internal class PropertyTags + { + public PropertyTags() + { + Enable = false; + Behavior = PropertyTagBehavior.Merge; + } + + /// + /// The behavior of how to save the tags assigned - + /// Merge (keep existing and append new), + /// Remove (remove any of the tags in the Tags property that are currently assigned, + /// Replace (replace the currently assigned tags with the ones specified) + /// + public PropertyTagBehavior Behavior { get; set; } + + /// + /// Flags the property to have tagging enabled + /// + public bool Enable { get; set; } + + /// + /// The actual tags to associate - tag/group + /// + public IEnumerable> Tags { get; set; } + + } + + internal enum PropertyTagBehavior + { + Replace, + Remove, + Merge + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentExtended.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentExtended.cs index 1a05c2e07a..4446a21329 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentExtended.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentExtended.cs @@ -1,46 +1,46 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Umbraco.Core.Models.PublishedContent -{ - /// - /// Provides methods to handle extended content. - /// - internal interface IPublishedContentExtended : IPublishedContent - { - /// - /// Adds a property to the extended content. - /// - /// The property to add. - void AddProperty(IPublishedProperty property); - - /// - /// Gets a value indicating whether properties were added to the extended content. - /// - bool HasAddedProperties { get; } - - /// - /// Sets the content set of the extended content. - /// - /// - void SetContentSet(IEnumerable contentSet); - - /// - /// Resets the content set of the extended content. - /// - void ClearContentSet(); - - /// - /// Sets the index of the extended content. - /// - /// The index value. - void SetIndex(int value); - - /// - /// Resets the index of the extended content. - /// - void ClearIndex(); - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Provides methods to handle extended content. + /// + internal interface IPublishedContentExtended : IPublishedContent + { + /// + /// Adds a property to the extended content. + /// + /// The property to add. + void AddProperty(IPublishedProperty property); + + /// + /// Gets a value indicating whether properties were added to the extended content. + /// + bool HasAddedProperties { get; } + + /// + /// Sets the content set of the extended content. + /// + /// + void SetContentSet(IEnumerable contentSet); + + /// + /// Resets the content set of the extended content. + /// + void ClearContentSet(); + + /// + /// Sets the index of the extended content. + /// + /// The index value. + void SetIndex(int value); + + /// + /// Resets the index of the extended content. + /// + void ClearIndex(); + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentModelFactory.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentModelFactory.cs index cd58a43b54..10a2410d34 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentModelFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentModelFactory.cs @@ -1,16 +1,16 @@ -namespace Umbraco.Core.Models.PublishedContent -{ - /// - /// Provides the model creation service. - /// - internal interface IPublishedContentModelFactory - { - /// - /// Creates a strongly-typed model representing a published content. - /// - /// The original published content. - /// The strongly-typed model representing the published content, or the published content - /// itself it the factory has no model for that content type. - IPublishedContent CreateModel(IPublishedContent content); - } +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Provides the model creation service. + /// + internal interface IPublishedContentModelFactory + { + /// + /// Creates a strongly-typed model representing a published content. + /// + /// The original published content. + /// The strongly-typed model representing the published content, or the published content + /// itself it the factory has no model for that content type. + IPublishedContent CreateModel(IPublishedContent content); + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs index 86ebb83919..da445ebec1 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs @@ -1,151 +1,151 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; - -namespace Umbraco.Core.Models.PublishedContent -{ - - public class PublishedContentExtended : PublishedContentWrapped, IPublishedContentExtended - { - #region Constructor - - // protected for models, private for our static Extend method - protected PublishedContentExtended(IPublishedContent content) - : base(content) - { } - - #endregion - - #region Index - - private int? _index; - - public override int GetIndex() - { - // fast - if (_index.HasValue) return _index.Value; - - // slow -- and don't cache, not in a set - if (_contentSet == null) return Content.GetIndex(); - - // slow -- but cache for next time - var index = _contentSet.FindIndex(x => x.Id == Id); - if (index < 0) - throw new IndexOutOfRangeException("Could not find content in the content set."); - _index = index; - return index; - } - - #endregion - - #region Extend - - internal static IPublishedContentExtended Extend(IPublishedContent content, IEnumerable contentSet) - { - var wrapped = content as PublishedContentExtended; - while (wrapped != null && ((IPublishedContentExtended)wrapped).HasAddedProperties == false) - wrapped = (content = wrapped.Unwrap()) as PublishedContentExtended; - - // if the factory returns something else than content it means it has created - // a model, and then that model has to inherit from PublishedContentExtended, - // => implements the internal IPublishedContentExtended. - - var model = PublishedContentModelFactory.CreateModel(content); - var extended = model == content // == means the factory did not create a model - ? new PublishedContentExtended(content) // so we have to extend - : model; // else we can use what the factory returned - - var extended2 = extended as IPublishedContentExtended; - if (extended2 != null) // always true, but keeps Resharper happy - extended2.SetContentSet(contentSet); - return extended2; - } - - #endregion - - #region IPublishedContentExtended - - void IPublishedContentExtended.AddProperty(IPublishedProperty property) - { - if (_properties == null) - _properties = new Collection(); - _properties.Add(property); - } - - bool IPublishedContentExtended.HasAddedProperties - { - get { return _properties != null; } - } - - void IPublishedContentExtended.SetContentSet(IEnumerable contentSet) - { - _contentSet = contentSet; - } - - void IPublishedContentExtended.ClearContentSet() - { - _contentSet = null; - } - - void IPublishedContentExtended.SetIndex(int value) - { - _index = value; - } - - void IPublishedContentExtended.ClearIndex() - { - _index = null; - } - - #endregion - - #region Content set - - private IEnumerable _contentSet; - - public override IEnumerable ContentSet - { - get { return _contentSet ?? Content.ContentSet; } - } - - #endregion - - #region Properties - - private ICollection _properties; - - public override ICollection Properties - { - get - { - return _properties == null - ? Content.Properties - : Content.Properties.Union(_properties).ToList(); - } - } - - public override object this[string alias] - { - get - { - if (_properties != null) - { - var property = _properties.FirstOrDefault(prop => prop.PropertyTypeAlias.InvariantEquals(alias)); - if (property != null) return property.HasValue ? property.Value : null; - } - return Content[alias]; - } - } - - public override IPublishedProperty GetProperty(string alias) - { - return _properties == null - ? Content.GetProperty(alias) - : _properties.FirstOrDefault(prop => prop.PropertyTypeAlias.InvariantEquals(alias)) ?? Content.GetProperty(alias); - } - - #endregion - } - -} +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +namespace Umbraco.Core.Models.PublishedContent +{ + + public class PublishedContentExtended : PublishedContentWrapped, IPublishedContentExtended + { + #region Constructor + + // protected for models, private for our static Extend method + protected PublishedContentExtended(IPublishedContent content) + : base(content) + { } + + #endregion + + #region Index + + private int? _index; + + public override int GetIndex() + { + // fast + if (_index.HasValue) return _index.Value; + + // slow -- and don't cache, not in a set + if (_contentSet == null) return Content.GetIndex(); + + // slow -- but cache for next time + var index = _contentSet.FindIndex(x => x.Id == Id); + if (index < 0) + throw new IndexOutOfRangeException("Could not find content in the content set."); + _index = index; + return index; + } + + #endregion + + #region Extend + + internal static IPublishedContentExtended Extend(IPublishedContent content, IEnumerable contentSet) + { + var wrapped = content as PublishedContentExtended; + while (wrapped != null && ((IPublishedContentExtended)wrapped).HasAddedProperties == false) + wrapped = (content = wrapped.Unwrap()) as PublishedContentExtended; + + // if the factory returns something else than content it means it has created + // a model, and then that model has to inherit from PublishedContentExtended, + // => implements the internal IPublishedContentExtended. + + var model = PublishedContentModelFactory.CreateModel(content); + var extended = model == content // == means the factory did not create a model + ? new PublishedContentExtended(content) // so we have to extend + : model; // else we can use what the factory returned + + var extended2 = extended as IPublishedContentExtended; + if (extended2 != null) // always true, but keeps Resharper happy + extended2.SetContentSet(contentSet); + return extended2; + } + + #endregion + + #region IPublishedContentExtended + + void IPublishedContentExtended.AddProperty(IPublishedProperty property) + { + if (_properties == null) + _properties = new Collection(); + _properties.Add(property); + } + + bool IPublishedContentExtended.HasAddedProperties + { + get { return _properties != null; } + } + + void IPublishedContentExtended.SetContentSet(IEnumerable contentSet) + { + _contentSet = contentSet; + } + + void IPublishedContentExtended.ClearContentSet() + { + _contentSet = null; + } + + void IPublishedContentExtended.SetIndex(int value) + { + _index = value; + } + + void IPublishedContentExtended.ClearIndex() + { + _index = null; + } + + #endregion + + #region Content set + + private IEnumerable _contentSet; + + public override IEnumerable ContentSet + { + get { return _contentSet ?? Content.ContentSet; } + } + + #endregion + + #region Properties + + private ICollection _properties; + + public override ICollection Properties + { + get + { + return _properties == null + ? Content.Properties + : Content.Properties.Union(_properties).ToList(); + } + } + + public override object this[string alias] + { + get + { + if (_properties != null) + { + var property = _properties.FirstOrDefault(prop => prop.PropertyTypeAlias.InvariantEquals(alias)); + if (property != null) return property.HasValue ? property.Value : null; + } + return Content[alias]; + } + } + + public override IPublishedProperty GetProperty(string alias) + { + return _properties == null + ? Content.GetProperty(alias) + : _properties.FirstOrDefault(prop => prop.PropertyTypeAlias.InvariantEquals(alias)) ?? Content.GetProperty(alias); + } + + #endregion + } + +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentModel.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModel.cs index eefea49a67..33a8c885ad 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentModel.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModel.cs @@ -1,24 +1,24 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Umbraco.Core.Models.PublishedContent -{ - /// - /// Represents a strongly-typed published content. - /// - /// Every strongly-typed published content class should inherit from PublishedContentModel - /// (or inherit from a class that inherits from... etc.) so they are picked by the factory. - internal abstract class PublishedContentModel : PublishedContentExtended - { - /// - /// Initializes a new instance of the class with - /// an original instance. - /// - /// The original content. - protected PublishedContentModel(IPublishedContent content) - : base(content) - { } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Represents a strongly-typed published content. + /// + /// Every strongly-typed published content class should inherit from PublishedContentModel + /// (or inherit from a class that inherits from... etc.) so they are picked by the factory. + internal abstract class PublishedContentModel : PublishedContentExtended + { + /// + /// Initializes a new instance of the class with + /// an original instance. + /// + /// The original content. + protected PublishedContentModel(IPublishedContent content) + : base(content) + { } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelAttribute.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelAttribute.cs index 64a9fe53b6..b49526b137 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelAttribute.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelAttribute.cs @@ -1,29 +1,29 @@ -using System; - -namespace Umbraco.Core.Models.PublishedContent -{ - /// - /// Indicates that the class is a published content model for a specified content type. - /// - /// By default, the name of the class is assumed to be the content type alias. The - /// PublishedContentModelAttribute can be used to indicate a different alias. - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - internal sealed class PublishedContentModelAttribute : Attribute - { - /// - /// Initializes a new instance of the class with a content type alias. - /// - /// The content type alias. - public PublishedContentModelAttribute(string contentTypeAlias) - { - if (string.IsNullOrWhiteSpace(contentTypeAlias)) - throw new ArgumentException("Argument cannot be null nor empty.", "contentTypeAlias"); - ContentTypeAlias = contentTypeAlias; - } - - /// - /// Gets or sets the content type alias. - /// - public string ContentTypeAlias { get; private set; } - } -} +using System; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Indicates that the class is a published content model for a specified content type. + /// + /// By default, the name of the class is assumed to be the content type alias. The + /// PublishedContentModelAttribute can be used to indicate a different alias. + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + internal sealed class PublishedContentModelAttribute : Attribute + { + /// + /// Initializes a new instance of the class with a content type alias. + /// + /// The content type alias. + public PublishedContentModelAttribute(string contentTypeAlias) + { + if (string.IsNullOrWhiteSpace(contentTypeAlias)) + throw new ArgumentException("Argument cannot be null nor empty.", "contentTypeAlias"); + ContentTypeAlias = contentTypeAlias; + } + + /// + /// Gets or sets the content type alias. + /// + public string ContentTypeAlias { get; private set; } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactory.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactory.cs index ce63a640e6..9ee414f2e5 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactory.cs @@ -1,20 +1,20 @@ -namespace Umbraco.Core.Models.PublishedContent -{ - /// - /// Provides strongly typed published content models services. - /// - internal static class PublishedContentModelFactory - { - /// - /// Creates a strongly typed published content model for an internal published content. - /// - /// The internal published content. - /// The strongly typed published content model. - public static IPublishedContent CreateModel(IPublishedContent content) - { - return PublishedContentModelFactoryResolver.Current.HasValue - ? PublishedContentModelFactoryResolver.Current.Factory.CreateModel(content) - : content; - } - } -} +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Provides strongly typed published content models services. + /// + internal static class PublishedContentModelFactory + { + /// + /// Creates a strongly typed published content model for an internal published content. + /// + /// The internal published content. + /// The strongly typed published content model. + public static IPublishedContent CreateModel(IPublishedContent content) + { + return PublishedContentModelFactoryResolver.Current.HasValue + ? PublishedContentModelFactoryResolver.Current.Factory.CreateModel(content) + : content; + } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactoryImpl.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactoryImpl.cs index 7bc0343add..39630646df 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactoryImpl.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactoryImpl.cs @@ -1,67 +1,67 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; - -namespace Umbraco.Core.Models.PublishedContent -{ - /// - /// Implements a strongly typed content model factory - /// - internal class PublishedContentModelFactoryImpl : IPublishedContentModelFactory - { - //private readonly Dictionary _constructors - // = new Dictionary(); - - private readonly Dictionary> _constructors - = new Dictionary>(); - - public PublishedContentModelFactoryImpl() - { - var types = PluginManager.Current.ResolveTypes(); - var ctorArgTypes = new[] { typeof(IPublishedContent) }; - - foreach (var type in types) - { - var constructor = type.GetConstructor(ctorArgTypes); - if (constructor == null) - throw new InvalidOperationException(string.Format("Type {0} is missing a public constructor with one argument of type IPublishedContent.", type.FullName)); - var attribute = type.GetCustomAttribute(false); - var typeName = attribute == null ? type.Name : attribute.ContentTypeAlias; - typeName = typeName.ToLowerInvariant(); - - if (_constructors.ContainsKey(typeName)) - throw new InvalidOperationException(string.Format("More that one type want to be a model for content type {0}.", typeName)); - - // should work everywhere, but slow - //_constructors[typeName] = constructor; - - // much faster with a dynamic method but potential MediumTrust issues - // here http://stackoverflow.com/questions/16363838/how-do-you-call-a-constructor-via-an-expression-tree-on-an-existing-object - - // fast enough and works in MediumTrust - // read http://boxbinary.com/2011/10/how-to-run-a-unit-test-in-medium-trust-with-nunitpart-three-umbraco-framework-testing/ - var exprArg = Expression.Parameter(typeof(IPublishedContent), "content"); - var exprNew = Expression.New(constructor, exprArg); - var expr = Expression.Lambda>(exprNew, exprArg); - var func = expr.Compile(); - _constructors[typeName] = func; - } - } - - public IPublishedContent CreateModel(IPublishedContent content) - { - // be case-insensitive - var contentTypeAlias = content.DocumentTypeAlias.ToLowerInvariant(); - - //ConstructorInfo constructor; - //return _constructors.TryGetValue(contentTypeAlias, out constructor) - // ? (IPublishedContent) constructor.Invoke(new object[] { content }) - // : content; - - Func constructor; - return _constructors.TryGetValue(contentTypeAlias, out constructor) - ? constructor(content) - : content; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Implements a strongly typed content model factory + /// + internal class PublishedContentModelFactoryImpl : IPublishedContentModelFactory + { + //private readonly Dictionary _constructors + // = new Dictionary(); + + private readonly Dictionary> _constructors + = new Dictionary>(); + + public PublishedContentModelFactoryImpl() + { + var types = PluginManager.Current.ResolveTypes(); + var ctorArgTypes = new[] { typeof(IPublishedContent) }; + + foreach (var type in types) + { + var constructor = type.GetConstructor(ctorArgTypes); + if (constructor == null) + throw new InvalidOperationException(string.Format("Type {0} is missing a public constructor with one argument of type IPublishedContent.", type.FullName)); + var attribute = type.GetCustomAttribute(false); + var typeName = attribute == null ? type.Name : attribute.ContentTypeAlias; + typeName = typeName.ToLowerInvariant(); + + if (_constructors.ContainsKey(typeName)) + throw new InvalidOperationException(string.Format("More that one type want to be a model for content type {0}.", typeName)); + + // should work everywhere, but slow + //_constructors[typeName] = constructor; + + // much faster with a dynamic method but potential MediumTrust issues + // here http://stackoverflow.com/questions/16363838/how-do-you-call-a-constructor-via-an-expression-tree-on-an-existing-object + + // fast enough and works in MediumTrust + // read http://boxbinary.com/2011/10/how-to-run-a-unit-test-in-medium-trust-with-nunitpart-three-umbraco-framework-testing/ + var exprArg = Expression.Parameter(typeof(IPublishedContent), "content"); + var exprNew = Expression.New(constructor, exprArg); + var expr = Expression.Lambda>(exprNew, exprArg); + var func = expr.Compile(); + _constructors[typeName] = func; + } + } + + public IPublishedContent CreateModel(IPublishedContent content) + { + // be case-insensitive + var contentTypeAlias = content.DocumentTypeAlias.ToLowerInvariant(); + + //ConstructorInfo constructor; + //return _constructors.TryGetValue(contentTypeAlias, out constructor) + // ? (IPublishedContent) constructor.Invoke(new object[] { content }) + // : content; + + Func constructor; + return _constructors.TryGetValue(contentTypeAlias, out constructor) + ? constructor(content) + : content; + } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactoryResolver.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactoryResolver.cs index 6995cafc7f..bcf576fd06 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactoryResolver.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactoryResolver.cs @@ -1,45 +1,45 @@ -using Umbraco.Core.ObjectResolution; - -namespace Umbraco.Core.Models.PublishedContent -{ - /// - /// Resolves the IPublishedContentModelFactory object. - /// - internal class PublishedContentModelFactoryResolver : SingleObjectResolverBase - { - /// - /// Initializes a new instance of the . - /// - /// The resolver is created by the WebBootManager and thus the constructor remains internal. - internal PublishedContentModelFactoryResolver() - : base() - { } - - /// - /// Initializes a new instance of the with a factory. - /// - /// The factory. - /// The resolver is created by the WebBootManager and thus the constructor remains internal. - internal PublishedContentModelFactoryResolver(IPublishedContentModelFactory factory) - : base(factory) - { } - - /// - /// Sets the factory. - /// - /// The factory. - /// For developers, at application startup. - public void SetFactory(IPublishedContentModelFactory factory) - { - Value = factory; - } - - /// - /// Gets the factory. - /// - public IPublishedContentModelFactory Factory - { - get { return Value; } - } - } +using Umbraco.Core.ObjectResolution; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Resolves the IPublishedContentModelFactory object. + /// + internal class PublishedContentModelFactoryResolver : SingleObjectResolverBase + { + /// + /// Initializes a new instance of the . + /// + /// The resolver is created by the WebBootManager and thus the constructor remains internal. + internal PublishedContentModelFactoryResolver() + : base() + { } + + /// + /// Initializes a new instance of the with a factory. + /// + /// The factory. + /// The resolver is created by the WebBootManager and thus the constructor remains internal. + internal PublishedContentModelFactoryResolver(IPublishedContentModelFactory factory) + : base(factory) + { } + + /// + /// Sets the factory. + /// + /// The factory. + /// For developers, at application startup. + public void SetFactory(IPublishedContentModelFactory factory) + { + Value = factory; + } + + /// + /// Gets the factory. + /// + public IPublishedContentModelFactory Factory + { + get { return Value; } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentOrderedSet.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentOrderedSet.cs index ffb67876e7..25956aa3d4 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentOrderedSet.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentOrderedSet.cs @@ -1,32 +1,32 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Umbraco.Core.Models.PublishedContent -{ - /// - /// Represents an ordered set of . - /// - /// The type of content. - public class PublishedContentOrderedSet : PublishedContentSet, IOrderedEnumerable - where T : class, IPublishedContent - { -// ReSharper disable ParameterTypeCanBeEnumerable.Local - internal PublishedContentOrderedSet(IOrderedEnumerable content) -// ReSharper restore ParameterTypeCanBeEnumerable.Local - : base(content) - { } - - // note: because we implement IOrderedEnumerable, we don't need to implement the ThenBy nor - // ThenByDescending methods here, only CreateOrderedEnumerable and that does it. - - #region IOrderedEnumerable - - public IOrderedEnumerable CreateOrderedEnumerable(Func keySelector, IComparer comparer, bool descending) - { - return new PublishedContentOrderedSet(((IOrderedEnumerable)Source).CreateOrderedEnumerable(keySelector, comparer, descending)); - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Represents an ordered set of . + /// + /// The type of content. + public class PublishedContentOrderedSet : PublishedContentSet, IOrderedEnumerable + where T : class, IPublishedContent + { +// ReSharper disable ParameterTypeCanBeEnumerable.Local + internal PublishedContentOrderedSet(IOrderedEnumerable content) +// ReSharper restore ParameterTypeCanBeEnumerable.Local + : base(content) + { } + + // note: because we implement IOrderedEnumerable, we don't need to implement the ThenBy nor + // ThenByDescending methods here, only CreateOrderedEnumerable and that does it. + + #region IOrderedEnumerable + + public IOrderedEnumerable CreateOrderedEnumerable(Func keySelector, IComparer comparer, bool descending) + { + return new PublishedContentOrderedSet(((IOrderedEnumerable)Source).CreateOrderedEnumerable(keySelector, comparer, descending)); + } + + #endregion + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentSet.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentSet.cs index bf4e983c7c..369a554674 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentSet.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentSet.cs @@ -1,234 +1,234 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Umbraco.Core.Models.PublishedContent -{ - /// - /// Represents a set of . - /// - /// The type of content. - /// - /// A ContentSet{T} is created from an IEnumerable{T} using the ToContentSet - /// extension method. - /// The content set source is enumerated only once. Same as what you get - /// when you call ToList on an IEnumerable. Only, ToList enumerates its source when - /// created, whereas a content set enumerates its source only when the content set itself - /// is enumerated. - /// - public class PublishedContentSet : IEnumerable - where T : class, IPublishedContent - { - // used by ToContentSet extension method to initialize a new set from an IEnumerable. - internal PublishedContentSet(IEnumerable source) - { - if (source == null) - throw new ArgumentNullException("source"); - Source = source; - } - - #region Source - - protected readonly IEnumerable Source; - - #endregion - - #region Enumerated - - // cache the enumeration so we don't enumerate more than once. Same as what you get - // when you call ToList on an IEnumerable. Only, ToList enumerates its source when - // created, whereas a content set enumerates its source only when the content set itself - // is enumerated. - - // cache the wrapped items so if we reset the enumeration, we do not re-wrap everything (only new items). - - private T[] _enumerated; - private readonly Dictionary _xContent = new Dictionary(); - - // wrap an item, ie create the actual clone for this set - private T MapContentAsT(T t) - { - return MapContent(t) as T; - } - - internal IPublishedContentExtended MapContent(T t) - { - IPublishedContentExtended extend; - if (_xContent.TryGetValue(t, out extend)) return extend; - - extend = PublishedContentExtended.Extend(t, this); - var asT = extend as T; - if (asT == null) - throw new InvalidOperationException(string.Format("Failed extend a published content of type {0}." - + "Got {1} when expecting {2}.", t.GetType().FullName, extend.GetType().FullName, typeof(T).FullName)); - _xContent[t] = extend; - return extend; - } - - private T[] Enumerated - { - get - { - // enumerate the source and cache the result - // tell clones about their index within the set (for perfs purposes) - var index = 0; - return _enumerated ?? (_enumerated = Source.Select(t => - { - var extend = MapContent(t); - extend.SetIndex(index++); - return extend as T; - }).ToArray()); - } - } - - // indicates that the source has changed - // so the set can clear its inner caches - // should only be used by DynamicPublishedContentList - internal void SourceChanged() - { - // reset the cached enumeration so it's enumerated again - if (_enumerated == null) return; - _enumerated = null; - - foreach (var item in _xContent.Values) - item.ClearIndex(); - - var removed = _xContent.Keys.Except(Source); - foreach (var content in removed) - { - _xContent[content].ClearContentSet(); - _xContent.Remove(content); - } - } - - /// - /// Gets the number of items in the set. - /// - /// The number of items in the set. - /// Will cause the set to be enumerated if it hasn't been already. - public virtual int Count - { - get { return Enumerated.Length; } - } - #endregion - - #region IEnumerable - - public IEnumerator GetEnumerator() - { - return ((IEnumerable)Enumerated).GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion - - #region Wrap methods returning T - - public T ElementAt(int index) - { - return MapContentAsT(Source.ElementAt(index)); - } - - public T ElementAtOrDefault(int index) - { - var element = Source.ElementAtOrDefault(index); - return element == null ? null : MapContentAsT(element); - } - - public T First() - { - return MapContentAsT(Source.First()); - } - - public T First(Func predicate) - { - return MapContentAsT(Source.First(predicate)); - } - - public T FirstOrDefault() - { - var first = Source.FirstOrDefault(); - return first == null ? null : MapContentAsT(first); - } - - public T FirstOrDefault(Func predicate) - { - var first = Source.FirstOrDefault(predicate); - return first == null ? null : MapContentAsT(first); - } - - public T Last() - { - return MapContentAsT(Source.Last()); - } - - public T Last(Func predicate) - { - return MapContentAsT(Source.Last(predicate)); - } - - public T LastOrDefault() - { - var last = Source.LastOrDefault(); - return last == null ? null : MapContentAsT(last); - } - - public T LastOrDefault(Func predicate) - { - var last = Source.LastOrDefault(predicate); - return last == null ? null : MapContentAsT(last); - } - - public T Single() - { - return MapContentAsT(Source.Single()); - } - - public T Single(Func predicate) - { - return MapContentAsT(Source.Single(predicate)); - } - - public T SingleOrDefault() - { - var single = Source.SingleOrDefault(); - return single == null ? null : MapContentAsT(single); - } - - public T SingleOrDefault(Func predicate) - { - var single = Source.SingleOrDefault(predicate); - return single == null ? null : MapContentAsT(single); - } - - #endregion - - #region Wrap methods returning IOrderedEnumerable - - public PublishedContentOrderedSet OrderBy(Func keySelector) - { - return new PublishedContentOrderedSet(Source.OrderBy(keySelector)); - } - - public PublishedContentOrderedSet OrderBy(Func keySelector, IComparer comparer) - { - return new PublishedContentOrderedSet(Source.OrderBy(keySelector, comparer)); - } - - public PublishedContentOrderedSet OrderByDescending(Func keySelector) - { - return new PublishedContentOrderedSet(Source.OrderByDescending(keySelector)); - } - - public PublishedContentOrderedSet OrderByDescending(Func keySelector, IComparer comparer) - { - return new PublishedContentOrderedSet(Source.OrderByDescending(keySelector, comparer)); - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Represents a set of . + /// + /// The type of content. + /// + /// A ContentSet{T} is created from an IEnumerable{T} using the ToContentSet + /// extension method. + /// The content set source is enumerated only once. Same as what you get + /// when you call ToList on an IEnumerable. Only, ToList enumerates its source when + /// created, whereas a content set enumerates its source only when the content set itself + /// is enumerated. + /// + public class PublishedContentSet : IEnumerable + where T : class, IPublishedContent + { + // used by ToContentSet extension method to initialize a new set from an IEnumerable. + internal PublishedContentSet(IEnumerable source) + { + if (source == null) + throw new ArgumentNullException("source"); + Source = source; + } + + #region Source + + protected readonly IEnumerable Source; + + #endregion + + #region Enumerated + + // cache the enumeration so we don't enumerate more than once. Same as what you get + // when you call ToList on an IEnumerable. Only, ToList enumerates its source when + // created, whereas a content set enumerates its source only when the content set itself + // is enumerated. + + // cache the wrapped items so if we reset the enumeration, we do not re-wrap everything (only new items). + + private T[] _enumerated; + private readonly Dictionary _xContent = new Dictionary(); + + // wrap an item, ie create the actual clone for this set + private T MapContentAsT(T t) + { + return MapContent(t) as T; + } + + internal IPublishedContentExtended MapContent(T t) + { + IPublishedContentExtended extend; + if (_xContent.TryGetValue(t, out extend)) return extend; + + extend = PublishedContentExtended.Extend(t, this); + var asT = extend as T; + if (asT == null) + throw new InvalidOperationException(string.Format("Failed extend a published content of type {0}." + + "Got {1} when expecting {2}.", t.GetType().FullName, extend.GetType().FullName, typeof(T).FullName)); + _xContent[t] = extend; + return extend; + } + + private T[] Enumerated + { + get + { + // enumerate the source and cache the result + // tell clones about their index within the set (for perfs purposes) + var index = 0; + return _enumerated ?? (_enumerated = Source.Select(t => + { + var extend = MapContent(t); + extend.SetIndex(index++); + return extend as T; + }).ToArray()); + } + } + + // indicates that the source has changed + // so the set can clear its inner caches + // should only be used by DynamicPublishedContentList + internal void SourceChanged() + { + // reset the cached enumeration so it's enumerated again + if (_enumerated == null) return; + _enumerated = null; + + foreach (var item in _xContent.Values) + item.ClearIndex(); + + var removed = _xContent.Keys.Except(Source); + foreach (var content in removed) + { + _xContent[content].ClearContentSet(); + _xContent.Remove(content); + } + } + + /// + /// Gets the number of items in the set. + /// + /// The number of items in the set. + /// Will cause the set to be enumerated if it hasn't been already. + public virtual int Count + { + get { return Enumerated.Length; } + } + #endregion + + #region IEnumerable + + public IEnumerator GetEnumerator() + { + return ((IEnumerable)Enumerated).GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + #region Wrap methods returning T + + public T ElementAt(int index) + { + return MapContentAsT(Source.ElementAt(index)); + } + + public T ElementAtOrDefault(int index) + { + var element = Source.ElementAtOrDefault(index); + return element == null ? null : MapContentAsT(element); + } + + public T First() + { + return MapContentAsT(Source.First()); + } + + public T First(Func predicate) + { + return MapContentAsT(Source.First(predicate)); + } + + public T FirstOrDefault() + { + var first = Source.FirstOrDefault(); + return first == null ? null : MapContentAsT(first); + } + + public T FirstOrDefault(Func predicate) + { + var first = Source.FirstOrDefault(predicate); + return first == null ? null : MapContentAsT(first); + } + + public T Last() + { + return MapContentAsT(Source.Last()); + } + + public T Last(Func predicate) + { + return MapContentAsT(Source.Last(predicate)); + } + + public T LastOrDefault() + { + var last = Source.LastOrDefault(); + return last == null ? null : MapContentAsT(last); + } + + public T LastOrDefault(Func predicate) + { + var last = Source.LastOrDefault(predicate); + return last == null ? null : MapContentAsT(last); + } + + public T Single() + { + return MapContentAsT(Source.Single()); + } + + public T Single(Func predicate) + { + return MapContentAsT(Source.Single(predicate)); + } + + public T SingleOrDefault() + { + var single = Source.SingleOrDefault(); + return single == null ? null : MapContentAsT(single); + } + + public T SingleOrDefault(Func predicate) + { + var single = Source.SingleOrDefault(predicate); + return single == null ? null : MapContentAsT(single); + } + + #endregion + + #region Wrap methods returning IOrderedEnumerable + + public PublishedContentOrderedSet OrderBy(Func keySelector) + { + return new PublishedContentOrderedSet(Source.OrderBy(keySelector)); + } + + public PublishedContentOrderedSet OrderBy(Func keySelector, IComparer comparer) + { + return new PublishedContentOrderedSet(Source.OrderBy(keySelector, comparer)); + } + + public PublishedContentOrderedSet OrderByDescending(Func keySelector) + { + return new PublishedContentOrderedSet(Source.OrderByDescending(keySelector)); + } + + public PublishedContentOrderedSet OrderByDescending(Func keySelector, IComparer comparer) + { + return new PublishedContentOrderedSet(Source.OrderByDescending(keySelector, comparer)); + } + + #endregion + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs index 587d88481b..5a3c79ebc1 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs @@ -1,172 +1,172 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Web.Caching; -using System.Web.UI; -using Umbraco.Core.Cache; - -namespace Umbraco.Core.Models.PublishedContent -{ - /// - /// Represents an type. - /// - /// Instances of the class are immutable, ie - /// if the content type changes, then a new class needs to be created. - public class PublishedContentType - { - private readonly PublishedPropertyType[] _propertyTypes; - - // fast alias-to-index xref containing both the raw alias and its lowercase version - private readonly Dictionary _indexes = new Dictionary(); - - // internal so it can be used by PublishedNoCache which does _not_ want to cache anything and so will never - // use the static cache getter PublishedContentType.GetPublishedContentType(alias) below - anything else - // should use it. - internal PublishedContentType(IContentTypeComposition contentType) - { - Id = contentType.Id; - Alias = contentType.Alias; - _propertyTypes = contentType.CompositionPropertyTypes - .Select(x => new PublishedPropertyType(this, x)) - .ToArray(); - InitializeIndexes(); - } - - // internal so it can be used for unit tests - internal PublishedContentType(int id, string alias, IEnumerable propertyTypes) - { - Id = id; - Alias = alias; - _propertyTypes = propertyTypes.ToArray(); - foreach (var propertyType in _propertyTypes) - propertyType.ContentType = this; - InitializeIndexes(); - } - - private void InitializeIndexes() - { - for (var i = 0; i < _propertyTypes.Length; i++) - { - var propertyType = _propertyTypes[i]; - _indexes[propertyType.PropertyTypeAlias] = i; - _indexes[propertyType.PropertyTypeAlias.ToLowerInvariant()] = i; - } - } - - #region Content type - - public int Id { get; private set; } - public string Alias { get; private set; } - - #endregion - - #region Properties - - public IEnumerable PropertyTypes - { - get { return _propertyTypes; } - } - - // alias is case-insensitive - // this is the ONLY place where we compare ALIASES! - public int GetPropertyIndex(string alias) - { - int index; - if (_indexes.TryGetValue(alias, out index)) return index; // fastest - if (_indexes.TryGetValue(alias.ToLowerInvariant(), out index)) return index; // slower - return -1; - } - - // virtual for unit tests - public virtual PublishedPropertyType GetPropertyType(string alias) - { - var index = GetPropertyIndex(alias); - return GetPropertyType(index); - } - - // virtual for unit tests - public virtual PublishedPropertyType GetPropertyType(int index) - { - return index >= 0 && index < _propertyTypes.Length ? _propertyTypes[index] : null; - } - - #endregion - - #region Cache - - // these methods are called by ContentTypeCacheRefresher and DataTypeCacheRefresher - - internal static void ClearAll() - { - Logging.LogHelper.Debug("Clear all."); - // ok and faster to do it by types, assuming noone else caches PublishedContentType instances - //ApplicationContext.Current.ApplicationCache.ClearStaticCacheByKeySearch("PublishedContentType_"); - ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes(); - } - - internal static void ClearContentType(int id) - { - Logging.LogHelper.Debug("Clear content type w/id {0}.", () => id); - // requires a predicate because the key does not contain the ID - // faster than key strings comparisons anyway - ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes( - (key, value) => value.Id == id); - } - - internal static void ClearDataType(int id) - { - Logging.LogHelper.Debug("Clear data type w/id {0}.", () => id); - // there is no recursion to handle here because a PublishedContentType contains *all* its - // properties ie both its own properties and those that were inherited (it's based upon an - // IContentTypeComposition) and so every PublishedContentType having a property based upon - // the cleared data type, be it local or inherited, will be cleared. - ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes( - (key, value) => value.PropertyTypes.Any(x => x.DataTypeId == id)); - } - - public static PublishedContentType Get(PublishedItemType itemType, string alias) - { - var key = string.Format("PublishedContentType_{0}_{1}", - itemType == PublishedItemType.Content ? "content" : "media", alias.ToLowerInvariant()); - - var type = ApplicationContext.Current.ApplicationCache.StaticCache.GetCacheItem(key, - () => CreatePublishedContentType(itemType, alias)); - - return type; - } - - private static PublishedContentType CreatePublishedContentType(PublishedItemType itemType, string alias) - { - if (GetPublishedContentTypeCallback != null) - return GetPublishedContentTypeCallback(alias); - - var contentType = itemType == PublishedItemType.Content - ? (IContentTypeComposition) ApplicationContext.Current.Services.ContentTypeService.GetContentType(alias) - : (IContentTypeComposition) ApplicationContext.Current.Services.ContentTypeService.GetMediaType(alias); - - if (contentType == null) - throw new Exception(string.Format("ContentTypeService failed to find a {0} type with alias \"{1}\".", - itemType.ToString().ToLower(), alias)); - - return new PublishedContentType(contentType); - } - - // for unit tests - changing the callback must reset the cache obviously - private static Func _getPublishedContentTypeCallBack; - internal static Func GetPublishedContentTypeCallback - { - get { return _getPublishedContentTypeCallBack; } - set - { - // see note above - //ClearAll(); - ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheByKeySearch("PublishedContentType_"); - - _getPublishedContentTypeCallBack = value; - } - } - - #endregion - } -} +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Web.Caching; +using System.Web.UI; +using Umbraco.Core.Cache; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Represents an type. + /// + /// Instances of the class are immutable, ie + /// if the content type changes, then a new class needs to be created. + public class PublishedContentType + { + private readonly PublishedPropertyType[] _propertyTypes; + + // fast alias-to-index xref containing both the raw alias and its lowercase version + private readonly Dictionary _indexes = new Dictionary(); + + // internal so it can be used by PublishedNoCache which does _not_ want to cache anything and so will never + // use the static cache getter PublishedContentType.GetPublishedContentType(alias) below - anything else + // should use it. + internal PublishedContentType(IContentTypeComposition contentType) + { + Id = contentType.Id; + Alias = contentType.Alias; + _propertyTypes = contentType.CompositionPropertyTypes + .Select(x => new PublishedPropertyType(this, x)) + .ToArray(); + InitializeIndexes(); + } + + // internal so it can be used for unit tests + internal PublishedContentType(int id, string alias, IEnumerable propertyTypes) + { + Id = id; + Alias = alias; + _propertyTypes = propertyTypes.ToArray(); + foreach (var propertyType in _propertyTypes) + propertyType.ContentType = this; + InitializeIndexes(); + } + + private void InitializeIndexes() + { + for (var i = 0; i < _propertyTypes.Length; i++) + { + var propertyType = _propertyTypes[i]; + _indexes[propertyType.PropertyTypeAlias] = i; + _indexes[propertyType.PropertyTypeAlias.ToLowerInvariant()] = i; + } + } + + #region Content type + + public int Id { get; private set; } + public string Alias { get; private set; } + + #endregion + + #region Properties + + public IEnumerable PropertyTypes + { + get { return _propertyTypes; } + } + + // alias is case-insensitive + // this is the ONLY place where we compare ALIASES! + public int GetPropertyIndex(string alias) + { + int index; + if (_indexes.TryGetValue(alias, out index)) return index; // fastest + if (_indexes.TryGetValue(alias.ToLowerInvariant(), out index)) return index; // slower + return -1; + } + + // virtual for unit tests + public virtual PublishedPropertyType GetPropertyType(string alias) + { + var index = GetPropertyIndex(alias); + return GetPropertyType(index); + } + + // virtual for unit tests + public virtual PublishedPropertyType GetPropertyType(int index) + { + return index >= 0 && index < _propertyTypes.Length ? _propertyTypes[index] : null; + } + + #endregion + + #region Cache + + // these methods are called by ContentTypeCacheRefresher and DataTypeCacheRefresher + + internal static void ClearAll() + { + Logging.LogHelper.Debug("Clear all."); + // ok and faster to do it by types, assuming noone else caches PublishedContentType instances + //ApplicationContext.Current.ApplicationCache.ClearStaticCacheByKeySearch("PublishedContentType_"); + ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes(); + } + + internal static void ClearContentType(int id) + { + Logging.LogHelper.Debug("Clear content type w/id {0}.", () => id); + // requires a predicate because the key does not contain the ID + // faster than key strings comparisons anyway + ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes( + (key, value) => value.Id == id); + } + + internal static void ClearDataType(int id) + { + Logging.LogHelper.Debug("Clear data type w/id {0}.", () => id); + // there is no recursion to handle here because a PublishedContentType contains *all* its + // properties ie both its own properties and those that were inherited (it's based upon an + // IContentTypeComposition) and so every PublishedContentType having a property based upon + // the cleared data type, be it local or inherited, will be cleared. + ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes( + (key, value) => value.PropertyTypes.Any(x => x.DataTypeId == id)); + } + + public static PublishedContentType Get(PublishedItemType itemType, string alias) + { + var key = string.Format("PublishedContentType_{0}_{1}", + itemType == PublishedItemType.Content ? "content" : "media", alias.ToLowerInvariant()); + + var type = ApplicationContext.Current.ApplicationCache.StaticCache.GetCacheItem(key, + () => CreatePublishedContentType(itemType, alias)); + + return type; + } + + private static PublishedContentType CreatePublishedContentType(PublishedItemType itemType, string alias) + { + if (GetPublishedContentTypeCallback != null) + return GetPublishedContentTypeCallback(alias); + + var contentType = itemType == PublishedItemType.Content + ? (IContentTypeComposition) ApplicationContext.Current.Services.ContentTypeService.GetContentType(alias) + : (IContentTypeComposition) ApplicationContext.Current.Services.ContentTypeService.GetMediaType(alias); + + if (contentType == null) + throw new Exception(string.Format("ContentTypeService failed to find a {0} type with alias \"{1}\".", + itemType.ToString().ToLower(), alias)); + + return new PublishedContentType(contentType); + } + + // for unit tests - changing the callback must reset the cache obviously + private static Func _getPublishedContentTypeCallBack; + internal static Func GetPublishedContentTypeCallback + { + get { return _getPublishedContentTypeCallBack; } + set + { + // see note above + //ClearAll(); + ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheByKeySearch("PublishedContentType_"); + + _getPublishedContentTypeCallBack = value; + } + } + + #endregion + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index 38e2537b40..2767dc6b8b 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -1,208 +1,208 @@ -using System; -using System.Collections.Generic; - -namespace Umbraco.Core.Models.PublishedContent -{ - // - // This class has two purposes. - // - // - First, we cannot implement strongly-typed content by inheriting from some sort - // of "master content" because that master content depends on the actual content cache - // that is being used. It can be an XmlPublishedContent with the XmlPublishedCache, - // or just anything else. - // - // So we implement strongly-typed content by encapsulating whatever content is - // returned by the content cache, and providing extra properties (mostly) or - // methods or whatever. This class provides the base for such encapsulation. - // - // - Second, any time a content is used in a content set obtained from - // IEnumerable.ToContentSet(), it needs to be cloned and extended - // in order to know about its position in the set. This class provides the base - // for implementing such extension. - // - - /// - /// Provides an abstract base class for IPublishedContent implementations that - /// wrap and extend another IPublishedContent. - /// - public abstract class PublishedContentWrapped : IPublishedContent - { - protected readonly IPublishedContent Content; - - /// - /// Initialize a new instance of the class - /// with an IPublishedContent instance to wrap and extend. - /// - /// The content to wrap and extend. - protected PublishedContentWrapped(IPublishedContent content) - { - Content = content; - } - - /// - /// Gets the wrapped content. - /// - /// The wrapped content, that was passed as an argument to the constructor. - public IPublishedContent Unwrap() - { - return Content; - } - - #region ContentSet - - public virtual IEnumerable ContentSet - { - get { return Content.ContentSet; } - } - - #endregion - - #region ContentType - - public virtual PublishedContentType ContentType { get { return Content.ContentType; } } - - #endregion - - #region Content - - public virtual int Id - { - get { return Content.Id; } - } - - public virtual int TemplateId - { - get { return Content.TemplateId; } - } - - public virtual int SortOrder - { - get { return Content.SortOrder; } - } - - public virtual string Name - { - get { return Content.Name; } - } - - public virtual string UrlName - { - get { return Content.UrlName; } - } - - public virtual string DocumentTypeAlias - { - get { return Content.DocumentTypeAlias; } - } - - public virtual int DocumentTypeId - { - get { return Content.DocumentTypeId; } - } - - public virtual string WriterName - { - get { return Content.WriterName; } - } - - public virtual string CreatorName - { - get { return Content.CreatorName; } - } - - public virtual int WriterId - { - get { return Content.WriterId; } - } - - public virtual int CreatorId - { - get { return Content.CreatorId; } - } - - public virtual string Path - { - get { return Content.Path; } - } - - public virtual DateTime CreateDate - { - get { return Content.CreateDate; } - } - - public virtual DateTime UpdateDate - { - get { return Content.UpdateDate; } - } - - public virtual Guid Version - { - get { return Content.Version; } - } - - public virtual int Level - { - get { return Content.Level; } - } - - public virtual string Url - { - get { return Content.Url; } - } - - public virtual PublishedItemType ItemType - { - get { return Content.ItemType; } - } - - public virtual bool IsDraft - { - get { return Content.IsDraft; } - } - - public virtual int GetIndex() - { - return Content.GetIndex(); - } - - #endregion - - #region Tree - - public virtual IPublishedContent Parent - { - get { return Content.Parent; } - } - - public virtual IEnumerable Children - { - get { return Content.Children; } - } - - #endregion - - #region Properties - - public virtual ICollection Properties - { - get { return Content.Properties; } - } - - public virtual object this[string alias] - { - get { return Content[alias]; } - } - - public virtual IPublishedProperty GetProperty(string alias) - { - return Content.GetProperty(alias); - } - - public virtual IPublishedProperty GetProperty(string alias, bool recurse) - { - return Content.GetProperty(alias, recurse); - } - - #endregion - } -} +using System; +using System.Collections.Generic; + +namespace Umbraco.Core.Models.PublishedContent +{ + // + // This class has two purposes. + // + // - First, we cannot implement strongly-typed content by inheriting from some sort + // of "master content" because that master content depends on the actual content cache + // that is being used. It can be an XmlPublishedContent with the XmlPublishedCache, + // or just anything else. + // + // So we implement strongly-typed content by encapsulating whatever content is + // returned by the content cache, and providing extra properties (mostly) or + // methods or whatever. This class provides the base for such encapsulation. + // + // - Second, any time a content is used in a content set obtained from + // IEnumerable.ToContentSet(), it needs to be cloned and extended + // in order to know about its position in the set. This class provides the base + // for implementing such extension. + // + + /// + /// Provides an abstract base class for IPublishedContent implementations that + /// wrap and extend another IPublishedContent. + /// + public abstract class PublishedContentWrapped : IPublishedContent + { + protected readonly IPublishedContent Content; + + /// + /// Initialize a new instance of the class + /// with an IPublishedContent instance to wrap and extend. + /// + /// The content to wrap and extend. + protected PublishedContentWrapped(IPublishedContent content) + { + Content = content; + } + + /// + /// Gets the wrapped content. + /// + /// The wrapped content, that was passed as an argument to the constructor. + public IPublishedContent Unwrap() + { + return Content; + } + + #region ContentSet + + public virtual IEnumerable ContentSet + { + get { return Content.ContentSet; } + } + + #endregion + + #region ContentType + + public virtual PublishedContentType ContentType { get { return Content.ContentType; } } + + #endregion + + #region Content + + public virtual int Id + { + get { return Content.Id; } + } + + public virtual int TemplateId + { + get { return Content.TemplateId; } + } + + public virtual int SortOrder + { + get { return Content.SortOrder; } + } + + public virtual string Name + { + get { return Content.Name; } + } + + public virtual string UrlName + { + get { return Content.UrlName; } + } + + public virtual string DocumentTypeAlias + { + get { return Content.DocumentTypeAlias; } + } + + public virtual int DocumentTypeId + { + get { return Content.DocumentTypeId; } + } + + public virtual string WriterName + { + get { return Content.WriterName; } + } + + public virtual string CreatorName + { + get { return Content.CreatorName; } + } + + public virtual int WriterId + { + get { return Content.WriterId; } + } + + public virtual int CreatorId + { + get { return Content.CreatorId; } + } + + public virtual string Path + { + get { return Content.Path; } + } + + public virtual DateTime CreateDate + { + get { return Content.CreateDate; } + } + + public virtual DateTime UpdateDate + { + get { return Content.UpdateDate; } + } + + public virtual Guid Version + { + get { return Content.Version; } + } + + public virtual int Level + { + get { return Content.Level; } + } + + public virtual string Url + { + get { return Content.Url; } + } + + public virtual PublishedItemType ItemType + { + get { return Content.ItemType; } + } + + public virtual bool IsDraft + { + get { return Content.IsDraft; } + } + + public virtual int GetIndex() + { + return Content.GetIndex(); + } + + #endregion + + #region Tree + + public virtual IPublishedContent Parent + { + get { return Content.Parent; } + } + + public virtual IEnumerable Children + { + get { return Content.Children; } + } + + #endregion + + #region Properties + + public virtual ICollection Properties + { + get { return Content.Properties; } + } + + public virtual object this[string alias] + { + get { return Content[alias]; } + } + + public virtual IPublishedProperty GetProperty(string alias) + { + return Content.GetProperty(alias); + } + + public virtual IPublishedProperty GetProperty(string alias, bool recurse) + { + return Content.GetProperty(alias, recurse); + } + + #endregion + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs index b841b85212..4bf89b49fb 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs @@ -1,31 +1,31 @@ -using System; - -namespace Umbraco.Core.Models.PublishedContent -{ - /// - /// Provides a base class for IPublishedProperty implementations which converts and caches - /// the value source to the actual value to use when rendering content. - /// - internal abstract class PublishedPropertyBase : IPublishedProperty - { - public readonly PublishedPropertyType PropertyType; - - protected PublishedPropertyBase(PublishedPropertyType propertyType) - { - if (propertyType == null) - throw new ArgumentNullException("propertyType"); - PropertyType = propertyType; - } - - public string PropertyTypeAlias - { - get { return PropertyType.PropertyTypeAlias; } - } - - // these have to be provided by the actual implementation - public abstract bool HasValue { get; } - public abstract object DataValue { get; } - public abstract object Value { get; } - public abstract object XPathValue { get; } - } -} +using System; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Provides a base class for IPublishedProperty implementations which converts and caches + /// the value source to the actual value to use when rendering content. + /// + internal abstract class PublishedPropertyBase : IPublishedProperty + { + public readonly PublishedPropertyType PropertyType; + + protected PublishedPropertyBase(PublishedPropertyType propertyType) + { + if (propertyType == null) + throw new ArgumentNullException("propertyType"); + PropertyType = propertyType; + } + + public string PropertyTypeAlias + { + get { return PropertyType.PropertyTypeAlias; } + } + + // these have to be provided by the actual implementation + public abstract bool HasValue { get; } + public abstract object DataValue { get; } + public abstract object Value { get; } + public abstract object XPathValue { get; } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs index 1c8b344298..b94a21a9bd 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs @@ -1,253 +1,253 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Xml.Linq; -using System.Xml.XPath; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Umbraco.Core.Dynamics; -using Umbraco.Core.PropertyEditors; - -namespace Umbraco.Core.Models.PublishedContent -{ - /// - /// Represents an type. - /// - /// Instances of the class are immutable, ie - /// if the property type changes, then a new class needs to be created. - public class PublishedPropertyType - { - public PublishedPropertyType(PublishedContentType contentType, PropertyType propertyType) - { - // PropertyEditor [1:n] DataTypeDefinition [1:n] PropertyType - - ContentType = contentType; - PropertyTypeAlias = propertyType.Alias; - - DataTypeId = propertyType.DataTypeDefinitionId; - PropertyEditorAlias = propertyType.PropertyEditorAlias; - - InitializeConverters(); - } - - // for unit tests - internal PublishedPropertyType(string propertyTypeAlias, int dataTypeDefinitionId, string propertyEditorAlias) - { - // ContentType to be set by PublishedContentType when creating it - PropertyTypeAlias = propertyTypeAlias; - - DataTypeId = dataTypeDefinitionId; - PropertyEditorAlias = propertyEditorAlias; - - InitializeConverters(); - } - - #region Property type - - /// - /// Gets or sets the published content type containing the property type. - /// - // internally set by PublishedContentType constructor - public PublishedContentType ContentType { get; internal set; } - - /// - /// Gets or sets the alias uniquely identifying the property type. - /// - public string PropertyTypeAlias { get; private set; } - - /// - /// Gets or sets the identifier uniquely identifying the data type supporting the property type. - /// - public int DataTypeId { get; private set; } - - /// - /// Gets or sets the alias uniquely identifying the property editor for the property type. - /// - public string PropertyEditorAlias { get; private set; } - - #endregion - - #region Converters - - private IPropertyValueConverter _converter; - - private PropertyCacheLevel _sourceCacheLevel; - private PropertyCacheLevel _objectCacheLevel; - private PropertyCacheLevel _xpathCacheLevel; - - private Type _clrType = typeof (object); - - private void InitializeConverters() - { - var converters = PropertyValueConvertersResolver.Current.Converters.ToArray(); - - // todo: remove Union() once we drop IPropertyEditorValueConverter support. - _converter = null; - foreach (var converter in converters.Union(GetCompatConverters()).Where(x => x.IsConverter(this))) - { - if (_converter == null) - { - _converter = converter; - } - else - { - throw new InvalidOperationException(string.Format("More than one converter for property type {0}.{1}", - ContentType.Alias, PropertyTypeAlias)); - } - } - - // get the cache levels, quietely fixing the inconsistencies (no need to throw, really) - _sourceCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Source); - _objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Object); - _objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.XPath); - if (_objectCacheLevel < _sourceCacheLevel) _objectCacheLevel = _sourceCacheLevel; - if (_xpathCacheLevel < _sourceCacheLevel) _xpathCacheLevel = _sourceCacheLevel; - - if (_converter != null) - { - var attr = _converter.GetType().GetCustomAttribute(false); - if (attr != null) - _clrType = attr.Type; - } - } - - static PropertyCacheLevel GetCacheLevel(IPropertyValueConverter converter, PropertyCacheValue value) - { - if (converter == null) - return PropertyCacheLevel.Request; - - var attr = converter.GetType().GetCustomAttributes(false) - .FirstOrDefault(x => x.Value == value || x.Value == PropertyCacheValue.All); - - return attr == null ? PropertyCacheLevel.Request : attr.Level; - } - - // converts the raw value into the source value - // uses converters, else falls back to dark (& performance-wise expensive) magic - // source: the property raw value - // preview: whether we are previewing or not - public object ConvertDataToSource(object source, bool preview) - { - // use the converter else use dark (& performance-wise expensive) magic - return _converter != null - ? _converter.ConvertDataToSource(this, source, preview) - : ConvertUsingDarkMagic(source); - } - - // gets the source cache level - public PropertyCacheLevel SourceCacheLevel { get { return _sourceCacheLevel; } } - - // converts the source value into the clr value - // uses converters, else returns the source value - // source: the property source value - // preview: whether we are previewing or not - public object ConvertSourceToObject(object source, bool preview) - { - // use the converter if any - // else just return the source value - return _converter != null - ? _converter.ConvertSourceToObject(this, source, preview) - : source; - } - - // gets the value cache level - public PropertyCacheLevel ObjectCacheLevel { get { return _objectCacheLevel; } } - - // converts the source value into the xpath value - // uses the converter else returns the source value as a string - // if successful, returns either a string or an XPathNavigator - // source: the property source value - // preview: whether we are previewing or not - public object ConvertSourceToXPath(object source, bool preview) - { - // use the converter if any - if (_converter != null) - return _converter.ConvertSourceToXPath(this, source, preview); - - // else just return the source value as a string or an XPathNavigator - if (source == null) return null; - var xElement = source as XElement; - if (xElement != null) - return xElement.CreateNavigator(); - return source.ToString().Trim(); - } - - // gets the xpath cache level - public PropertyCacheLevel XPathCacheLevel { get { return _xpathCacheLevel; } } - - internal static object ConvertUsingDarkMagic(object source) - { - // convert to string - var stringSource = source as string; - if (stringSource == null) return source; // not a string => return the object - stringSource = stringSource.Trim(); - if (stringSource.Length == 0) return null; // empty string => return null - - // try numbers and booleans - // make sure we use the invariant culture ie a dot decimal point, comma is for csv - // NOTE far from perfect: "01a" is returned as a string but "012" is returned as an integer... - int i; - if (int.TryParse(stringSource, NumberStyles.Integer, CultureInfo.InvariantCulture, out i)) - return i; - float f; - if (float.TryParse(stringSource, NumberStyles.Float, CultureInfo.InvariantCulture, out f)) - return f; - bool b; - if (bool.TryParse(stringSource, out b)) - return b; - - //TODO: We can change this just like we do for the JSON converter - but to maintain compatibility might mean this still has to remain here - - // try xml - that is expensive, performance-wise - XElement elt; - if (XmlHelper.TryCreateXElementFromPropertyValue(stringSource, out elt)) - return new DynamicXml(elt); // xml => return DynamicXml for compatiblity's sake - - return source; - } - - // gets the property CLR type - public Type ClrType { get { return _clrType; } } - - #endregion - - #region Compat - - // backward-compatibility: support IPropertyEditorValueConverter while we have to - // todo: remove once we drop IPropertyEditorValueConverter support. - - IEnumerable GetCompatConverters() - { - var propertyEditorGuid = LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias(PropertyEditorAlias, LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.ReturnNull); - return PropertyEditorValueConvertersResolver.HasCurrent && propertyEditorGuid.HasValue - ? PropertyEditorValueConvertersResolver.Current.Converters - .Where(x => x.IsConverterFor(propertyEditorGuid.Value, ContentType.Alias, PropertyTypeAlias)) - .Select(x => new CompatConverter(x)) - : Enumerable.Empty(); - } - - class CompatConverter : PropertyValueConverterBase - { - private readonly IPropertyEditorValueConverter _converter; - - public CompatConverter(IPropertyEditorValueConverter converter) - { - _converter = converter; - } - - public override bool IsConverter(PublishedPropertyType propertyType) - { - return true; - } - - public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) - { - // NOTE: ignore preview, because IPropertyEditorValueConverter does not support it - return _converter.ConvertPropertyValue(source).Result; - } - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Xml.Linq; +using System.Xml.XPath; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Umbraco.Core.Dynamics; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Represents an type. + /// + /// Instances of the class are immutable, ie + /// if the property type changes, then a new class needs to be created. + public class PublishedPropertyType + { + public PublishedPropertyType(PublishedContentType contentType, PropertyType propertyType) + { + // PropertyEditor [1:n] DataTypeDefinition [1:n] PropertyType + + ContentType = contentType; + PropertyTypeAlias = propertyType.Alias; + + DataTypeId = propertyType.DataTypeDefinitionId; + PropertyEditorAlias = propertyType.PropertyEditorAlias; + + InitializeConverters(); + } + + // for unit tests + internal PublishedPropertyType(string propertyTypeAlias, int dataTypeDefinitionId, string propertyEditorAlias) + { + // ContentType to be set by PublishedContentType when creating it + PropertyTypeAlias = propertyTypeAlias; + + DataTypeId = dataTypeDefinitionId; + PropertyEditorAlias = propertyEditorAlias; + + InitializeConverters(); + } + + #region Property type + + /// + /// Gets or sets the published content type containing the property type. + /// + // internally set by PublishedContentType constructor + public PublishedContentType ContentType { get; internal set; } + + /// + /// Gets or sets the alias uniquely identifying the property type. + /// + public string PropertyTypeAlias { get; private set; } + + /// + /// Gets or sets the identifier uniquely identifying the data type supporting the property type. + /// + public int DataTypeId { get; private set; } + + /// + /// Gets or sets the alias uniquely identifying the property editor for the property type. + /// + public string PropertyEditorAlias { get; private set; } + + #endregion + + #region Converters + + private IPropertyValueConverter _converter; + + private PropertyCacheLevel _sourceCacheLevel; + private PropertyCacheLevel _objectCacheLevel; + private PropertyCacheLevel _xpathCacheLevel; + + private Type _clrType = typeof (object); + + private void InitializeConverters() + { + var converters = PropertyValueConvertersResolver.Current.Converters.ToArray(); + + // todo: remove Union() once we drop IPropertyEditorValueConverter support. + _converter = null; + foreach (var converter in converters.Union(GetCompatConverters()).Where(x => x.IsConverter(this))) + { + if (_converter == null) + { + _converter = converter; + } + else + { + throw new InvalidOperationException(string.Format("More than one converter for property type {0}.{1}", + ContentType.Alias, PropertyTypeAlias)); + } + } + + // get the cache levels, quietely fixing the inconsistencies (no need to throw, really) + _sourceCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Source); + _objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Object); + _objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.XPath); + if (_objectCacheLevel < _sourceCacheLevel) _objectCacheLevel = _sourceCacheLevel; + if (_xpathCacheLevel < _sourceCacheLevel) _xpathCacheLevel = _sourceCacheLevel; + + if (_converter != null) + { + var attr = _converter.GetType().GetCustomAttribute(false); + if (attr != null) + _clrType = attr.Type; + } + } + + static PropertyCacheLevel GetCacheLevel(IPropertyValueConverter converter, PropertyCacheValue value) + { + if (converter == null) + return PropertyCacheLevel.Request; + + var attr = converter.GetType().GetCustomAttributes(false) + .FirstOrDefault(x => x.Value == value || x.Value == PropertyCacheValue.All); + + return attr == null ? PropertyCacheLevel.Request : attr.Level; + } + + // converts the raw value into the source value + // uses converters, else falls back to dark (& performance-wise expensive) magic + // source: the property raw value + // preview: whether we are previewing or not + public object ConvertDataToSource(object source, bool preview) + { + // use the converter else use dark (& performance-wise expensive) magic + return _converter != null + ? _converter.ConvertDataToSource(this, source, preview) + : ConvertUsingDarkMagic(source); + } + + // gets the source cache level + public PropertyCacheLevel SourceCacheLevel { get { return _sourceCacheLevel; } } + + // converts the source value into the clr value + // uses converters, else returns the source value + // source: the property source value + // preview: whether we are previewing or not + public object ConvertSourceToObject(object source, bool preview) + { + // use the converter if any + // else just return the source value + return _converter != null + ? _converter.ConvertSourceToObject(this, source, preview) + : source; + } + + // gets the value cache level + public PropertyCacheLevel ObjectCacheLevel { get { return _objectCacheLevel; } } + + // converts the source value into the xpath value + // uses the converter else returns the source value as a string + // if successful, returns either a string or an XPathNavigator + // source: the property source value + // preview: whether we are previewing or not + public object ConvertSourceToXPath(object source, bool preview) + { + // use the converter if any + if (_converter != null) + return _converter.ConvertSourceToXPath(this, source, preview); + + // else just return the source value as a string or an XPathNavigator + if (source == null) return null; + var xElement = source as XElement; + if (xElement != null) + return xElement.CreateNavigator(); + return source.ToString().Trim(); + } + + // gets the xpath cache level + public PropertyCacheLevel XPathCacheLevel { get { return _xpathCacheLevel; } } + + internal static object ConvertUsingDarkMagic(object source) + { + // convert to string + var stringSource = source as string; + if (stringSource == null) return source; // not a string => return the object + stringSource = stringSource.Trim(); + if (stringSource.Length == 0) return null; // empty string => return null + + // try numbers and booleans + // make sure we use the invariant culture ie a dot decimal point, comma is for csv + // NOTE far from perfect: "01a" is returned as a string but "012" is returned as an integer... + int i; + if (int.TryParse(stringSource, NumberStyles.Integer, CultureInfo.InvariantCulture, out i)) + return i; + float f; + if (float.TryParse(stringSource, NumberStyles.Float, CultureInfo.InvariantCulture, out f)) + return f; + bool b; + if (bool.TryParse(stringSource, out b)) + return b; + + //TODO: We can change this just like we do for the JSON converter - but to maintain compatibility might mean this still has to remain here + + // try xml - that is expensive, performance-wise + XElement elt; + if (XmlHelper.TryCreateXElementFromPropertyValue(stringSource, out elt)) + return new DynamicXml(elt); // xml => return DynamicXml for compatiblity's sake + + return source; + } + + // gets the property CLR type + public Type ClrType { get { return _clrType; } } + + #endregion + + #region Compat + + // backward-compatibility: support IPropertyEditorValueConverter while we have to + // todo: remove once we drop IPropertyEditorValueConverter support. + + IEnumerable GetCompatConverters() + { + var propertyEditorGuid = LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias(PropertyEditorAlias, LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.ReturnNull); + return PropertyEditorValueConvertersResolver.HasCurrent && propertyEditorGuid.HasValue + ? PropertyEditorValueConvertersResolver.Current.Converters + .Where(x => x.IsConverterFor(propertyEditorGuid.Value, ContentType.Alias, PropertyTypeAlias)) + .Select(x => new CompatConverter(x)) + : Enumerable.Empty(); + } + + class CompatConverter : PropertyValueConverterBase + { + private readonly IPropertyEditorValueConverter _converter; + + public CompatConverter(IPropertyEditorValueConverter converter) + { + _converter = converter; + } + + public override bool IsConverter(PublishedPropertyType propertyType) + { + return true; + } + + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + // NOTE: ignore preview, because IPropertyEditorValueConverter does not support it + return _converter.ConvertPropertyValue(source).Result; + } + } + + #endregion + } +} diff --git a/src/Umbraco.Core/Models/Rdbms/MemberTypeReadOnlyDto.cs b/src/Umbraco.Core/Models/Rdbms/MemberTypeReadOnlyDto.cs index 3694588f56..4833cb33f0 100644 --- a/src/Umbraco.Core/Models/Rdbms/MemberTypeReadOnlyDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/MemberTypeReadOnlyDto.cs @@ -1,78 +1,78 @@ -using System; -using System.Collections.Generic; -using Umbraco.Core.Persistence; - -namespace Umbraco.Core.Models.Rdbms -{ - [TableName("umbracoNode")] - [PrimaryKey("id")] - [ExplicitColumns] - internal class MemberTypeReadOnlyDto - { - /* from umbracoNode */ - [Column("id")] - public int NodeId { get; set; } - - [Column("trashed")] - public bool Trashed { get; set; } - - [Column("parentID")] - public int ParentId { get; set; } - - [Column("nodeUser")] - public int? UserId { get; set; } - - [Column("level")] - public short Level { get; set; } - - [Column("path")] - public string Path { get; set; } - - [Column("sortOrder")] - public int SortOrder { get; set; } - - [Column("uniqueID")] - public Guid? UniqueId { get; set; } - - [Column("text")] - public string Text { get; set; } - - [Column("nodeObjectType")] - public Guid? NodeObjectType { get; set; } - - [Column("createDate")] - public DateTime CreateDate { get; set; } - - /* cmsContentType */ - [Column("pk")] - public int PrimaryKey { get; set; } - - [Column("alias")] - public string Alias { get; set; } - - [Column("icon")] - public string Icon { get; set; } - - [Column("thumbnail")] - public string Thumbnail { get; set; } - - [Column("description")] - public string Description { get; set; } - - [Column("isContainer")] - public bool IsContainer { get; set; } - - [Column("allowAtRoot")] - public bool AllowAtRoot { get; set; } - - /* PropertyTypes */ - //TODO Add PropertyTypeDto (+MemberTypeDto and DataTypeDto as one) ReadOnly list - [ResultColumn] - public List PropertyTypes { get; set; } - - /* PropertyTypeGroups */ - //TODO Add PropertyTypeGroupDto ReadOnly list - [ResultColumn] - public List PropertyTypeGroups { get; set; } - } +using System; +using System.Collections.Generic; +using Umbraco.Core.Persistence; + +namespace Umbraco.Core.Models.Rdbms +{ + [TableName("umbracoNode")] + [PrimaryKey("id")] + [ExplicitColumns] + internal class MemberTypeReadOnlyDto + { + /* from umbracoNode */ + [Column("id")] + public int NodeId { get; set; } + + [Column("trashed")] + public bool Trashed { get; set; } + + [Column("parentID")] + public int ParentId { get; set; } + + [Column("nodeUser")] + public int? UserId { get; set; } + + [Column("level")] + public short Level { get; set; } + + [Column("path")] + public string Path { get; set; } + + [Column("sortOrder")] + public int SortOrder { get; set; } + + [Column("uniqueID")] + public Guid? UniqueId { get; set; } + + [Column("text")] + public string Text { get; set; } + + [Column("nodeObjectType")] + public Guid? NodeObjectType { get; set; } + + [Column("createDate")] + public DateTime CreateDate { get; set; } + + /* cmsContentType */ + [Column("pk")] + public int PrimaryKey { get; set; } + + [Column("alias")] + public string Alias { get; set; } + + [Column("icon")] + public string Icon { get; set; } + + [Column("thumbnail")] + public string Thumbnail { get; set; } + + [Column("description")] + public string Description { get; set; } + + [Column("isContainer")] + public bool IsContainer { get; set; } + + [Column("allowAtRoot")] + public bool AllowAtRoot { get; set; } + + /* PropertyTypes */ + //TODO Add PropertyTypeDto (+MemberTypeDto and DataTypeDto as one) ReadOnly list + [ResultColumn] + public List PropertyTypes { get; set; } + + /* PropertyTypeGroups */ + //TODO Add PropertyTypeGroupDto ReadOnly list + [ResultColumn] + public List PropertyTypeGroups { get; set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupReadOnlyDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupReadOnlyDto.cs index 01736a270d..8dcc4af29c 100644 --- a/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupReadOnlyDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupReadOnlyDto.cs @@ -1,25 +1,25 @@ -using Umbraco.Core.Persistence; - -namespace Umbraco.Core.Models.Rdbms -{ - [TableName("cmsPropertyTypeGroup")] - [PrimaryKey("id", autoIncrement = true)] - [ExplicitColumns] - internal class PropertyTypeGroupReadOnlyDto - { - [Column("PropertyTypeGroupId")] - public int? Id { get; set; } - - [Column("parentGroupId")] - public int? ParentGroupId { get; set; } - - [Column("PropertyGroupName")] - public string Text { get; set; } - - [Column("PropertyGroupSortOrder")] - public int SortOrder { get; set; } - - [Column("contenttypeNodeId")] - public int ContentTypeNodeId { get; set; } - } +using Umbraco.Core.Persistence; + +namespace Umbraco.Core.Models.Rdbms +{ + [TableName("cmsPropertyTypeGroup")] + [PrimaryKey("id", autoIncrement = true)] + [ExplicitColumns] + internal class PropertyTypeGroupReadOnlyDto + { + [Column("PropertyTypeGroupId")] + public int? Id { get; set; } + + [Column("parentGroupId")] + public int? ParentGroupId { get; set; } + + [Column("PropertyGroupName")] + public string Text { get; set; } + + [Column("PropertyGroupSortOrder")] + public int SortOrder { get; set; } + + [Column("contenttypeNodeId")] + public int ContentTypeNodeId { get; set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyTypeReadOnlyDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyTypeReadOnlyDto.cs index f46c4e9dae..1b6145f3ad 100644 --- a/src/Umbraco.Core/Models/Rdbms/PropertyTypeReadOnlyDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/PropertyTypeReadOnlyDto.cs @@ -1,58 +1,58 @@ -using System; -using Umbraco.Core.Persistence; - -namespace Umbraco.Core.Models.Rdbms -{ - [TableName("cmsPropertyType")] - [PrimaryKey("id")] - [ExplicitColumns] - internal class PropertyTypeReadOnlyDto - { - [Column("PropertyTypeId")] - public int? Id { get; set; } - - [Column("dataTypeId")] - public int DataTypeId { get; set; } - - [Column("contentTypeId")] - public int ContentTypeId { get; set; } - - [Column("PropertyTypesGroupId")] - public int? PropertyTypeGroupId { get; set; } - - [Column("Alias")] - public string Alias { get; set; } - - [Column("Name")] - public string Name { get; set; } - - [Column("helpText")] - public string HelpText { get; set; } - - [Column("PropertyTypeSortOrder")] - public int SortOrder { get; set; } - - [Column("mandatory")] - public bool Mandatory { get; set; } - - [Column("validationRegExp")] - public string ValidationRegExp { get; set; } - - [Column("Description")] - public string Description { get; set; } - - /* cmsMemberType */ - [Column("memberCanEdit")] - public bool CanEdit { get; set; } - - [Column("viewOnProfile")] - public bool ViewOnProfile { get; set; } - - /* cmsDataType */ - [Column("propertyEditorAlias")] - public string PropertyEditorAlias { get; set; } - - [Column("dbType")] - public string DbType { get; set; } - } +using System; +using Umbraco.Core.Persistence; + +namespace Umbraco.Core.Models.Rdbms +{ + [TableName("cmsPropertyType")] + [PrimaryKey("id")] + [ExplicitColumns] + internal class PropertyTypeReadOnlyDto + { + [Column("PropertyTypeId")] + public int? Id { get; set; } + + [Column("dataTypeId")] + public int DataTypeId { get; set; } + + [Column("contentTypeId")] + public int ContentTypeId { get; set; } + + [Column("PropertyTypesGroupId")] + public int? PropertyTypeGroupId { get; set; } + + [Column("Alias")] + public string Alias { get; set; } + + [Column("Name")] + public string Name { get; set; } + + [Column("helpText")] + public string HelpText { get; set; } + + [Column("PropertyTypeSortOrder")] + public int SortOrder { get; set; } + + [Column("mandatory")] + public bool Mandatory { get; set; } + + [Column("validationRegExp")] + public string ValidationRegExp { get; set; } + + [Column("Description")] + public string Description { get; set; } + + /* cmsMemberType */ + [Column("memberCanEdit")] + public bool CanEdit { get; set; } + + [Column("viewOnProfile")] + public bool ViewOnProfile { get; set; } + + /* cmsDataType */ + [Column("propertyEditorAlias")] + public string PropertyEditorAlias { get; set; } + + [Column("dbType")] + public string DbType { get; set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Tag.cs b/src/Umbraco.Core/Models/Tag.cs index e730e46918..e84b68b063 100644 --- a/src/Umbraco.Core/Models/Tag.cs +++ b/src/Umbraco.Core/Models/Tag.cs @@ -1,57 +1,57 @@ -using System; -using System.Reflection; -using System.Runtime.Serialization; -using Umbraco.Core.Models.EntityBase; - -namespace Umbraco.Core.Models -{ - [Serializable] - [DataContract(IsReference = true)] - public class Tag : Entity, ITag - { - public Tag() - { - } - - public Tag(int id, string text, string @group) - { - Text = text; - Group = @group; - Id = id; - } - - private static readonly PropertyInfo TextSelector = ExpressionHelper.GetPropertyInfo(x => x.Text); - private static readonly PropertyInfo GroupSelector = ExpressionHelper.GetPropertyInfo(x => x.Group); - private string _text; - private string _group; - - public string Text - { - get { return _text; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _text = value; - return _text; - }, _text, TextSelector); - } - } - - public string Group - { - get { return _group; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _group = value; - return _group; - }, _group, GroupSelector); - } - } - - //TODO: enable this at some stage - //public int ParentId { get; set; } - } +using System; +using System.Reflection; +using System.Runtime.Serialization; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models +{ + [Serializable] + [DataContract(IsReference = true)] + public class Tag : Entity, ITag + { + public Tag() + { + } + + public Tag(int id, string text, string @group) + { + Text = text; + Group = @group; + Id = id; + } + + private static readonly PropertyInfo TextSelector = ExpressionHelper.GetPropertyInfo(x => x.Text); + private static readonly PropertyInfo GroupSelector = ExpressionHelper.GetPropertyInfo(x => x.Group); + private string _text; + private string _group; + + public string Text + { + get { return _text; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _text = value; + return _text; + }, _text, TextSelector); + } + } + + public string Group + { + get { return _group; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _group = value; + return _group; + }, _group, GroupSelector); + } + } + + //TODO: enable this at some stage + //public int ParentId { get; set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/TaggableObjectTypes.cs b/src/Umbraco.Core/Models/TaggableObjectTypes.cs index 4e9b70fd3e..be019410e5 100644 --- a/src/Umbraco.Core/Models/TaggableObjectTypes.cs +++ b/src/Umbraco.Core/Models/TaggableObjectTypes.cs @@ -1,12 +1,12 @@ -namespace Umbraco.Core.Models -{ - /// - /// Enum representing the taggable object types - /// - public enum TaggableObjectTypes - { - Content, - Media, - Member - } +namespace Umbraco.Core.Models +{ + /// + /// Enum representing the taggable object types + /// + public enum TaggableObjectTypes + { + Content, + Media, + Member + } } \ No newline at end of file diff --git a/src/Umbraco.Core/ObservableDictionary.cs b/src/Umbraco.Core/ObservableDictionary.cs index 29ba500805..b665071386 100644 --- a/src/Umbraco.Core/ObservableDictionary.cs +++ b/src/Umbraco.Core/ObservableDictionary.cs @@ -1,150 +1,150 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Umbraco.Core -{ - /// - /// An ObservableDictionary - /// - /// - /// Assumes that the key will not change and is unique for each element in the collection. - /// Collection is not thread-safe, so calls should be made single-threaded. - /// - /// The type of elements contained in the BindableCollection - /// The type of the indexing key - public class ObservableDictionary : ObservableCollection - { - protected Dictionary Indecies = new Dictionary(); - protected Func KeySelector; - - /// - /// Create new ObservableDictionary - /// - /// Selector function to create key from value - public ObservableDictionary(Func keySelector) - : base() - { - if (keySelector == null) throw new ArgumentException("keySelector"); - KeySelector = keySelector; - } - - #region Protected Methods - protected override void InsertItem(int index, TValue item) - { - var key = KeySelector(item); - if (Indecies.ContainsKey(key)) - throw new DuplicateKeyException(key.ToString()); - - if (index != this.Count) - { - foreach (var k in Indecies.Keys.Where(k => Indecies[k] >= index).ToList()) - { - Indecies[k]++; - } - } - - base.InsertItem(index, item); - Indecies[key] = index; - - } - - protected override void ClearItems() - { - base.ClearItems(); - Indecies.Clear(); - } - - - protected override void RemoveItem(int index) - { - var item = this[index]; - var key = KeySelector(item); - - base.RemoveItem(index); - - Indecies.Remove(key); - - foreach (var k in Indecies.Keys.Where(k => Indecies[k] > index).ToList()) - { - Indecies[k]--; - } - } - #endregion - - public virtual bool ContainsKey(TKey key) - { - return Indecies.ContainsKey(key); - } - - /// - /// Gets or sets the element with the specified key. If setting a new value, new value must have same key. - /// - /// Key of element to replace - /// - public virtual TValue this[TKey key] - { - - get { return this[Indecies[key]]; } - set - { - //confirm key matches - if (!KeySelector(value).Equals(key)) - throw new InvalidOperationException("Key of new value does not match"); - - if (!Indecies.ContainsKey(key)) - { - this.Add(value); - } - else - { - this[Indecies[key]] = value; - } - } - } - - /// - /// Replaces element at given key with new value. New value must have same key. - /// - /// Key of element to replace - /// New value - /// - /// - /// False if key not found - public virtual bool Replace(TKey key, TValue value) - { - if (!Indecies.ContainsKey(key)) return false; - //confirm key matches - if (!KeySelector(value).Equals(key)) - throw new InvalidOperationException("Key of new value does not match"); - - this[Indecies[key]] = value; - return true; - - } - - public virtual bool Remove(TKey key) - { - if (!Indecies.ContainsKey(key)) return false; - - this.RemoveAt(Indecies[key]); - return true; - - } - - internal class DuplicateKeyException : Exception - { - - public string Key { get; private set; } - public DuplicateKeyException(string key) - : base("Attempted to insert duplicate key " + key + " in collection") - { - Key = key; - } - } - - } -} +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Core +{ + /// + /// An ObservableDictionary + /// + /// + /// Assumes that the key will not change and is unique for each element in the collection. + /// Collection is not thread-safe, so calls should be made single-threaded. + /// + /// The type of elements contained in the BindableCollection + /// The type of the indexing key + public class ObservableDictionary : ObservableCollection + { + protected Dictionary Indecies = new Dictionary(); + protected Func KeySelector; + + /// + /// Create new ObservableDictionary + /// + /// Selector function to create key from value + public ObservableDictionary(Func keySelector) + : base() + { + if (keySelector == null) throw new ArgumentException("keySelector"); + KeySelector = keySelector; + } + + #region Protected Methods + protected override void InsertItem(int index, TValue item) + { + var key = KeySelector(item); + if (Indecies.ContainsKey(key)) + throw new DuplicateKeyException(key.ToString()); + + if (index != this.Count) + { + foreach (var k in Indecies.Keys.Where(k => Indecies[k] >= index).ToList()) + { + Indecies[k]++; + } + } + + base.InsertItem(index, item); + Indecies[key] = index; + + } + + protected override void ClearItems() + { + base.ClearItems(); + Indecies.Clear(); + } + + + protected override void RemoveItem(int index) + { + var item = this[index]; + var key = KeySelector(item); + + base.RemoveItem(index); + + Indecies.Remove(key); + + foreach (var k in Indecies.Keys.Where(k => Indecies[k] > index).ToList()) + { + Indecies[k]--; + } + } + #endregion + + public virtual bool ContainsKey(TKey key) + { + return Indecies.ContainsKey(key); + } + + /// + /// Gets or sets the element with the specified key. If setting a new value, new value must have same key. + /// + /// Key of element to replace + /// + public virtual TValue this[TKey key] + { + + get { return this[Indecies[key]]; } + set + { + //confirm key matches + if (!KeySelector(value).Equals(key)) + throw new InvalidOperationException("Key of new value does not match"); + + if (!Indecies.ContainsKey(key)) + { + this.Add(value); + } + else + { + this[Indecies[key]] = value; + } + } + } + + /// + /// Replaces element at given key with new value. New value must have same key. + /// + /// Key of element to replace + /// New value + /// + /// + /// False if key not found + public virtual bool Replace(TKey key, TValue value) + { + if (!Indecies.ContainsKey(key)) return false; + //confirm key matches + if (!KeySelector(value).Equals(key)) + throw new InvalidOperationException("Key of new value does not match"); + + this[Indecies[key]] = value; + return true; + + } + + public virtual bool Remove(TKey key) + { + if (!Indecies.ContainsKey(key)) return false; + + this.RemoveAt(Indecies[key]); + return true; + + } + + internal class DuplicateKeyException : Exception + { + + public string Key { get; private set; } + public DuplicateKeyException(string key) + : base("Attempted to insert duplicate key " + key + " in collection") + { + Key = key; + } + } + + } +} diff --git a/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs b/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs index 9e68cda0fb..2b9ada9278 100644 --- a/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs +++ b/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs @@ -1,228 +1,228 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Security; -using System.Security.Permissions; -using System.Text; -using System.Threading.Tasks; -using System.Web; -using Umbraco.Core.Logging; - -namespace Umbraco.Core.Packaging -{ - internal class PackageBinaryInspector : MarshalByRefObject - { - /// - /// Entry point to call from your code - /// - /// - /// - /// - /// - /// - /// Will perform the assembly scan in a separate app domain - /// - public static IEnumerable ScanAssembliesForTypeReference(string dllPath, out string[] errorReport) - { - var appDomain = GetTempAppDomain(); - var type = typeof(PackageBinaryInspector); - try - { - var value = (PackageBinaryInspector)appDomain.CreateInstanceAndUnwrap( - type.Assembly.FullName, - type.FullName); - var result = value.PerformScan(dllPath, out errorReport); - return result; - } - finally - { - AppDomain.Unload(appDomain); - } - } - - /// - /// Performs the assembly scanning - /// - /// - /// - /// - /// - /// - /// This method is executed in a separate app domain - /// - internal IEnumerable PerformScan(string dllPath, out string[] errorReport) - { - if (Directory.Exists(dllPath) == false) - { - throw new DirectoryNotFoundException("Could not find directory " + dllPath); - } - - var files = Directory.GetFiles(dllPath, "*.dll"); - var dllsWithReference = new List(); - var errors = new List(); - var assembliesWithErrors = new List(); - - //we need this handler to resolve assembly dependencies below - AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) => - { - var a = Assembly.ReflectionOnlyLoad(e.Name); - if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name); - return a; - }; - - //First load each dll file into the context - var loaded = files.Select(Assembly.ReflectionOnlyLoadFrom).ToList(); - - //load each of the LoadFrom assemblies into the Load context too - foreach (var a in loaded) - { - Assembly.ReflectionOnlyLoad(a.FullName); - } - - //get the list of assembly names to compare below - var loadedNames = loaded.Select(x => x.GetName().Name).ToArray(); - - //Then load each referenced assembly into the context - foreach (var a in loaded) - { - //don't load any referenced assemblies that are already found in the loaded array - this is based on name - // regardless of version. We'll assume that if the assembly found in the folder matches the assembly name - // being looked for, that is the version the user has shipped their package with and therefore it 'must' be correct - foreach (var assemblyName in a.GetReferencedAssemblies().Where(ass => loadedNames.Contains(ass.Name) == false)) - { - try - { - Assembly.ReflectionOnlyLoad(assemblyName.FullName); - } - catch (FileNotFoundException) - { - //if an exception occurs it means that a referenced assembly could not be found - errors.Add( - string.Concat("This package references the assembly '", - assemblyName.Name, - "' which was not found")); - assembliesWithErrors.Add(a); - } - catch (Exception ex) - { - //if an exception occurs it means that a referenced assembly could not be found - errors.Add( - string.Concat("This package could not be verified for compatibility. An error occurred while loading a referenced assembly '", - assemblyName.Name, - "' see error log for full details.")); - assembliesWithErrors.Add(a); - LogHelper.Error("An error occurred scanning package assemblies", ex); - } - } - } - - var contractType = GetLoadFromContractType(); - - //now that we have all referenced types into the context we can look up stuff - foreach (var a in loaded.Except(assembliesWithErrors)) - { - //now we need to see if they contain any type 'T' - var reflectedAssembly = a; - - try - { - var found = reflectedAssembly.GetExportedTypes() - .Where(contractType.IsAssignableFrom); - - if (found.Any()) - { - dllsWithReference.Add(reflectedAssembly.FullName); - } - } - catch (Exception ex) - { - //This is a hack that nobody can seem to get around, I've read everything and it seems that - // this is quite a common thing when loading types into reflection only load context, so - // we're just going to ignore this specific one for now - var typeLoadEx = ex as TypeLoadException; - if (typeLoadEx != null) - { - if (typeLoadEx.Message.InvariantContains("does not have an implementation")) - { - //ignore - continue; - } - } - else - { - errors.Add( - string.Concat("This package could not be verified for compatibility. An error occurred while scanning a packaged assembly '", - a.GetName().Name, - "' see error log for full details.")); - assembliesWithErrors.Add(a); - LogHelper.Error("An error occurred scanning package assemblies", ex); - } - } - - } - - errorReport = errors.ToArray(); - return dllsWithReference; - } - - - /// - /// In order to compare types, the types must be in the same context, this method will return the type that - /// we are checking against but from the Load context. - /// - /// - /// - private static Type GetLoadFromContractType() - { - var contractAssemblyLoadFrom =Assembly.ReflectionOnlyLoad(typeof (T).Assembly.FullName); - - var contractType = contractAssemblyLoadFrom.GetExportedTypes() - .FirstOrDefault(x => x.FullName == typeof(T).FullName && x.Assembly.FullName == typeof(T).Assembly.FullName); - - if (contractType == null) - { - throw new InvalidOperationException("Could not find type " + typeof(T) + " in the LoadFrom assemblies"); - } - return contractType; - } - - /// - /// Create an app domain - /// - /// - private static AppDomain GetTempAppDomain() - { - //copy the current app domain setup but don't shadow copy files - var appName = "TempDomain" + Guid.NewGuid(); - var domainSetup = new AppDomainSetup - { - ApplicationName = appName, - ShadowCopyFiles = "false", - ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase, - ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, - DynamicBase = AppDomain.CurrentDomain.SetupInformation.DynamicBase, - LicenseFile = AppDomain.CurrentDomain.SetupInformation.LicenseFile, - LoaderOptimization = AppDomain.CurrentDomain.SetupInformation.LoaderOptimization, - PrivateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath, - PrivateBinPathProbe = AppDomain.CurrentDomain.SetupInformation.PrivateBinPathProbe - }; - - //create new domain with full trust - return AppDomain.CreateDomain( - appName, - AppDomain.CurrentDomain.Evidence, - domainSetup, - new PermissionSet(PermissionState.Unrestricted)); - } - - private static string GetAssemblyPath(Assembly a) - { - var codeBase = a.CodeBase; - var uri = new Uri(codeBase); - return uri.LocalPath; - } - - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Security; +using System.Security.Permissions; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using Umbraco.Core.Logging; + +namespace Umbraco.Core.Packaging +{ + internal class PackageBinaryInspector : MarshalByRefObject + { + /// + /// Entry point to call from your code + /// + /// + /// + /// + /// + /// + /// Will perform the assembly scan in a separate app domain + /// + public static IEnumerable ScanAssembliesForTypeReference(string dllPath, out string[] errorReport) + { + var appDomain = GetTempAppDomain(); + var type = typeof(PackageBinaryInspector); + try + { + var value = (PackageBinaryInspector)appDomain.CreateInstanceAndUnwrap( + type.Assembly.FullName, + type.FullName); + var result = value.PerformScan(dllPath, out errorReport); + return result; + } + finally + { + AppDomain.Unload(appDomain); + } + } + + /// + /// Performs the assembly scanning + /// + /// + /// + /// + /// + /// + /// This method is executed in a separate app domain + /// + internal IEnumerable PerformScan(string dllPath, out string[] errorReport) + { + if (Directory.Exists(dllPath) == false) + { + throw new DirectoryNotFoundException("Could not find directory " + dllPath); + } + + var files = Directory.GetFiles(dllPath, "*.dll"); + var dllsWithReference = new List(); + var errors = new List(); + var assembliesWithErrors = new List(); + + //we need this handler to resolve assembly dependencies below + AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) => + { + var a = Assembly.ReflectionOnlyLoad(e.Name); + if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name); + return a; + }; + + //First load each dll file into the context + var loaded = files.Select(Assembly.ReflectionOnlyLoadFrom).ToList(); + + //load each of the LoadFrom assemblies into the Load context too + foreach (var a in loaded) + { + Assembly.ReflectionOnlyLoad(a.FullName); + } + + //get the list of assembly names to compare below + var loadedNames = loaded.Select(x => x.GetName().Name).ToArray(); + + //Then load each referenced assembly into the context + foreach (var a in loaded) + { + //don't load any referenced assemblies that are already found in the loaded array - this is based on name + // regardless of version. We'll assume that if the assembly found in the folder matches the assembly name + // being looked for, that is the version the user has shipped their package with and therefore it 'must' be correct + foreach (var assemblyName in a.GetReferencedAssemblies().Where(ass => loadedNames.Contains(ass.Name) == false)) + { + try + { + Assembly.ReflectionOnlyLoad(assemblyName.FullName); + } + catch (FileNotFoundException) + { + //if an exception occurs it means that a referenced assembly could not be found + errors.Add( + string.Concat("This package references the assembly '", + assemblyName.Name, + "' which was not found")); + assembliesWithErrors.Add(a); + } + catch (Exception ex) + { + //if an exception occurs it means that a referenced assembly could not be found + errors.Add( + string.Concat("This package could not be verified for compatibility. An error occurred while loading a referenced assembly '", + assemblyName.Name, + "' see error log for full details.")); + assembliesWithErrors.Add(a); + LogHelper.Error("An error occurred scanning package assemblies", ex); + } + } + } + + var contractType = GetLoadFromContractType(); + + //now that we have all referenced types into the context we can look up stuff + foreach (var a in loaded.Except(assembliesWithErrors)) + { + //now we need to see if they contain any type 'T' + var reflectedAssembly = a; + + try + { + var found = reflectedAssembly.GetExportedTypes() + .Where(contractType.IsAssignableFrom); + + if (found.Any()) + { + dllsWithReference.Add(reflectedAssembly.FullName); + } + } + catch (Exception ex) + { + //This is a hack that nobody can seem to get around, I've read everything and it seems that + // this is quite a common thing when loading types into reflection only load context, so + // we're just going to ignore this specific one for now + var typeLoadEx = ex as TypeLoadException; + if (typeLoadEx != null) + { + if (typeLoadEx.Message.InvariantContains("does not have an implementation")) + { + //ignore + continue; + } + } + else + { + errors.Add( + string.Concat("This package could not be verified for compatibility. An error occurred while scanning a packaged assembly '", + a.GetName().Name, + "' see error log for full details.")); + assembliesWithErrors.Add(a); + LogHelper.Error("An error occurred scanning package assemblies", ex); + } + } + + } + + errorReport = errors.ToArray(); + return dllsWithReference; + } + + + /// + /// In order to compare types, the types must be in the same context, this method will return the type that + /// we are checking against but from the Load context. + /// + /// + /// + private static Type GetLoadFromContractType() + { + var contractAssemblyLoadFrom =Assembly.ReflectionOnlyLoad(typeof (T).Assembly.FullName); + + var contractType = contractAssemblyLoadFrom.GetExportedTypes() + .FirstOrDefault(x => x.FullName == typeof(T).FullName && x.Assembly.FullName == typeof(T).Assembly.FullName); + + if (contractType == null) + { + throw new InvalidOperationException("Could not find type " + typeof(T) + " in the LoadFrom assemblies"); + } + return contractType; + } + + /// + /// Create an app domain + /// + /// + private static AppDomain GetTempAppDomain() + { + //copy the current app domain setup but don't shadow copy files + var appName = "TempDomain" + Guid.NewGuid(); + var domainSetup = new AppDomainSetup + { + ApplicationName = appName, + ShadowCopyFiles = "false", + ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase, + ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, + DynamicBase = AppDomain.CurrentDomain.SetupInformation.DynamicBase, + LicenseFile = AppDomain.CurrentDomain.SetupInformation.LicenseFile, + LoaderOptimization = AppDomain.CurrentDomain.SetupInformation.LoaderOptimization, + PrivateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath, + PrivateBinPathProbe = AppDomain.CurrentDomain.SetupInformation.PrivateBinPathProbe + }; + + //create new domain with full trust + return AppDomain.CreateDomain( + appName, + AppDomain.CurrentDomain.Evidence, + domainSetup, + new PermissionSet(PermissionState.Unrestricted)); + } + + private static string GetAssemblyPath(Assembly a) + { + var codeBase = a.CodeBase; + var uri = new Uri(codeBase); + return uri.LocalPath; + } + + } +} diff --git a/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs b/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs index 206244d260..dec58a09f1 100644 --- a/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs @@ -1,72 +1,72 @@ -using System.Collections.Generic; -using System.Globalization; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Rdbms; - -namespace Umbraco.Core.Persistence.Factories -{ - internal class MacroFactory : IEntityFactory - { - #region Implementation of IEntityFactory - - public IMacro BuildEntity(MacroDto dto) - { - var model = new Macro(dto.Id, dto.UseInEditor, dto.RefreshRate, dto.Alias, dto.Name, dto.ScriptType, dto.ScriptAssembly, dto.Xslt, dto.CacheByPage, dto.CachePersonalized, dto.DontRender, dto.Python); - foreach (var p in dto.MacroPropertyDtos) - { - model.Properties.Add(new MacroProperty(p.Id, p.Alias, p.Name, p.SortOrder, p.EditorAlias)); - } - - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - model.ResetDirtyProperties(false); - return model; - } - - public MacroDto BuildDto(IMacro entity) - { - var dto = new MacroDto() - { - Alias = entity.Alias, - CacheByPage = entity.CacheByPage, - CachePersonalized = entity.CacheByMember, - DontRender = entity.DontRender, - Name = entity.Name, - Python = entity.ScriptPath, - RefreshRate = entity.CacheDuration, - ScriptAssembly = entity.ControlAssembly, - ScriptType = entity.ControlType, - UseInEditor = entity.UseInEditor, - Xslt = entity.XsltPath, - MacroPropertyDtos = BuildPropertyDtos(entity) - }; - - if (entity.HasIdentity) - dto.Id = short.Parse(entity.Id.ToString(CultureInfo.InvariantCulture)); - - return dto; - } - - #endregion - - private List BuildPropertyDtos(IMacro entity) - { - var list = new List(); - foreach (var p in entity.Properties) - { - var text = new MacroPropertyDto - { - Alias = p.Alias, - Name = p.Name, - Macro = entity.Id, - SortOrder = (byte)p.SortOrder, - EditorAlias = p.EditorAlias, - Id = p.Id - }; - - list.Add(text); - } - return list; - } - } +using System.Collections.Generic; +using System.Globalization; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Factories +{ + internal class MacroFactory : IEntityFactory + { + #region Implementation of IEntityFactory + + public IMacro BuildEntity(MacroDto dto) + { + var model = new Macro(dto.Id, dto.UseInEditor, dto.RefreshRate, dto.Alias, dto.Name, dto.ScriptType, dto.ScriptAssembly, dto.Xslt, dto.CacheByPage, dto.CachePersonalized, dto.DontRender, dto.Python); + foreach (var p in dto.MacroPropertyDtos) + { + model.Properties.Add(new MacroProperty(p.Id, p.Alias, p.Name, p.SortOrder, p.EditorAlias)); + } + + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + model.ResetDirtyProperties(false); + return model; + } + + public MacroDto BuildDto(IMacro entity) + { + var dto = new MacroDto() + { + Alias = entity.Alias, + CacheByPage = entity.CacheByPage, + CachePersonalized = entity.CacheByMember, + DontRender = entity.DontRender, + Name = entity.Name, + Python = entity.ScriptPath, + RefreshRate = entity.CacheDuration, + ScriptAssembly = entity.ControlAssembly, + ScriptType = entity.ControlType, + UseInEditor = entity.UseInEditor, + Xslt = entity.XsltPath, + MacroPropertyDtos = BuildPropertyDtos(entity) + }; + + if (entity.HasIdentity) + dto.Id = short.Parse(entity.Id.ToString(CultureInfo.InvariantCulture)); + + return dto; + } + + #endregion + + private List BuildPropertyDtos(IMacro entity) + { + var list = new List(); + foreach (var p in entity.Properties) + { + var text = new MacroPropertyDto + { + Alias = p.Alias, + Name = p.Name, + Macro = entity.Id, + SortOrder = (byte)p.SortOrder, + EditorAlias = p.EditorAlias, + Id = p.Id + }; + + list.Add(text); + } + return list; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Factories/MemberFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberFactory.cs index 1aa8f70bb8..188a834b62 100644 --- a/src/Umbraco.Core/Persistence/Factories/MemberFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MemberFactory.cs @@ -1,93 +1,93 @@ -using System; -using System.Globalization; -using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Rdbms; - -namespace Umbraco.Core.Persistence.Factories -{ - internal class MemberFactory : IEntityFactory - { - private readonly Guid _nodeObjectTypeId; - private readonly int _id; - private int _primaryKey; - - public MemberFactory(Guid nodeObjectTypeId, int id) - { - _nodeObjectTypeId = nodeObjectTypeId; - _id = id; - } - - public IMember BuildEntity(MemberDto dto) - { - throw new System.NotImplementedException(); - } - - public MemberDto BuildDto(IMember entity) - { - var member = new MemberDto - { - NodeId = entity.Id, - Email = entity.Email, - LoginName = entity.Username, - Password = entity.Password, - ContentVersionDto = BuildDto(entity as Member) - }; - return member; - } - - public void SetPrimaryKey(int primaryKey) - { - _primaryKey = primaryKey; - } - - private ContentVersionDto BuildDto(Member entity) - { - var dto = new ContentVersionDto - { - NodeId = entity.Id, - VersionDate = entity.UpdateDate, - VersionId = entity.Version, - ContentDto = BuildContentDto(entity) - }; - return dto; - } - - private ContentDto BuildContentDto(Member entity) - { - var contentDto = new ContentDto - { - NodeId = entity.Id, - ContentTypeId = entity.ContentTypeId, - NodeDto = BuildNodeDto(entity) - }; - - if (_primaryKey > 0) - { - contentDto.PrimaryKey = _primaryKey; - } - - return contentDto; - } - - private NodeDto BuildNodeDto(IUmbracoEntity entity) - { - var nodeDto = new NodeDto - { - CreateDate = entity.CreateDate, - NodeId = entity.Id, - Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)), - NodeObjectType = _nodeObjectTypeId, - ParentId = entity.ParentId, - Path = entity.Path, - SortOrder = entity.SortOrder, - Text = entity.Name, - Trashed = entity.Trashed, - UniqueId = entity.Key, - UserId = entity.CreatorId - }; - - return nodeDto; - } - } +using System; +using System.Globalization; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Factories +{ + internal class MemberFactory : IEntityFactory + { + private readonly Guid _nodeObjectTypeId; + private readonly int _id; + private int _primaryKey; + + public MemberFactory(Guid nodeObjectTypeId, int id) + { + _nodeObjectTypeId = nodeObjectTypeId; + _id = id; + } + + public IMember BuildEntity(MemberDto dto) + { + throw new System.NotImplementedException(); + } + + public MemberDto BuildDto(IMember entity) + { + var member = new MemberDto + { + NodeId = entity.Id, + Email = entity.Email, + LoginName = entity.Username, + Password = entity.Password, + ContentVersionDto = BuildDto(entity as Member) + }; + return member; + } + + public void SetPrimaryKey(int primaryKey) + { + _primaryKey = primaryKey; + } + + private ContentVersionDto BuildDto(Member entity) + { + var dto = new ContentVersionDto + { + NodeId = entity.Id, + VersionDate = entity.UpdateDate, + VersionId = entity.Version, + ContentDto = BuildContentDto(entity) + }; + return dto; + } + + private ContentDto BuildContentDto(Member entity) + { + var contentDto = new ContentDto + { + NodeId = entity.Id, + ContentTypeId = entity.ContentTypeId, + NodeDto = BuildNodeDto(entity) + }; + + if (_primaryKey > 0) + { + contentDto.PrimaryKey = _primaryKey; + } + + return contentDto; + } + + private NodeDto BuildNodeDto(IUmbracoEntity entity) + { + var nodeDto = new NodeDto + { + CreateDate = entity.CreateDate, + NodeId = entity.Id, + Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)), + NodeObjectType = _nodeObjectTypeId, + ParentId = entity.ParentId, + Path = entity.Path, + SortOrder = entity.SortOrder, + Text = entity.Name, + Trashed = entity.Trashed, + UniqueId = entity.Key, + UserId = entity.CreatorId + }; + + return nodeDto; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs index 16879a7871..225b6173f5 100644 --- a/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs @@ -1,73 +1,73 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Rdbms; - -namespace Umbraco.Core.Persistence.Factories -{ - internal class MemberReadOnlyFactory : IEntityFactory - { - private readonly IDictionary _memberTypes; - - public MemberReadOnlyFactory(IDictionary memberTypes) - { - _memberTypes = memberTypes; - } - - public IMember BuildEntity(MemberReadOnlyDto dto) - { - var properties = CreateProperties(_memberTypes[dto.ContentTypeAlias], dto.Properties, dto.CreateDate); - - var member = new Member(dto.Text, dto.Email, dto.LoginName, dto.Password, dto.ParentId, _memberTypes[dto.ContentTypeAlias]) - { - Id = dto.NodeId, - CreateDate = dto.CreateDate, - UpdateDate = dto.UpdateDate, - Name = dto.Text, - ProviderUserKey = dto.UniqueId, - Trashed = dto.Trashed, - Key = dto.UniqueId.Value, - CreatorId = dto.UserId.HasValue ? dto.UserId.Value : 0, - Level = dto.Level, - Path = dto.Path, - SortOrder = dto.SortOrder, - Version = dto.VersionId, - ContentTypeAlias = dto.ContentTypeAlias, - Properties = new PropertyCollection(properties) - }; - - member.SetProviderUserKeyType(typeof(Guid)); - member.ResetDirtyProperties(false); - return member; - } - - public MemberReadOnlyDto BuildDto(IMember entity) - { - throw new System.NotImplementedException(); - } - - public IEnumerable CreateProperties(IMemberType memberType, IEnumerable dtos, DateTime createDate) - { - var properties = new List(); - - foreach (var propertyType in memberType.CompositionPropertyTypes) - { - var propertyDataDto = dtos.LastOrDefault(x => x.PropertyTypeId == propertyType.Id); - var property = propertyDataDto == null - ? propertyType.CreatePropertyFromValue(null) - : propertyType.CreatePropertyFromRawValue(propertyDataDto.GetValue, - propertyDataDto.VersionId, - propertyDataDto.PropertyDataId.Value); - //on initial construction we don't want to have dirty properties tracked - property.CreateDate = createDate; - property.UpdateDate = createDate; - // http://issues.umbraco.org/issue/U4-1946 - property.ResetDirtyProperties(false); - properties.Add(property); - } - - return properties; - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Factories +{ + internal class MemberReadOnlyFactory : IEntityFactory + { + private readonly IDictionary _memberTypes; + + public MemberReadOnlyFactory(IDictionary memberTypes) + { + _memberTypes = memberTypes; + } + + public IMember BuildEntity(MemberReadOnlyDto dto) + { + var properties = CreateProperties(_memberTypes[dto.ContentTypeAlias], dto.Properties, dto.CreateDate); + + var member = new Member(dto.Text, dto.Email, dto.LoginName, dto.Password, dto.ParentId, _memberTypes[dto.ContentTypeAlias]) + { + Id = dto.NodeId, + CreateDate = dto.CreateDate, + UpdateDate = dto.UpdateDate, + Name = dto.Text, + ProviderUserKey = dto.UniqueId, + Trashed = dto.Trashed, + Key = dto.UniqueId.Value, + CreatorId = dto.UserId.HasValue ? dto.UserId.Value : 0, + Level = dto.Level, + Path = dto.Path, + SortOrder = dto.SortOrder, + Version = dto.VersionId, + ContentTypeAlias = dto.ContentTypeAlias, + Properties = new PropertyCollection(properties) + }; + + member.SetProviderUserKeyType(typeof(Guid)); + member.ResetDirtyProperties(false); + return member; + } + + public MemberReadOnlyDto BuildDto(IMember entity) + { + throw new System.NotImplementedException(); + } + + public IEnumerable CreateProperties(IMemberType memberType, IEnumerable dtos, DateTime createDate) + { + var properties = new List(); + + foreach (var propertyType in memberType.CompositionPropertyTypes) + { + var propertyDataDto = dtos.LastOrDefault(x => x.PropertyTypeId == propertyType.Id); + var property = propertyDataDto == null + ? propertyType.CreatePropertyFromValue(null) + : propertyType.CreatePropertyFromRawValue(propertyDataDto.GetValue, + propertyDataDto.VersionId, + propertyDataDto.PropertyDataId.Value); + //on initial construction we don't want to have dirty properties tracked + property.CreateDate = createDate; + property.UpdateDate = createDate; + // http://issues.umbraco.org/issue/U4-1946 + property.ResetDirtyProperties(false); + properties.Add(property); + } + + return properties; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Factories/MemberTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberTypeFactory.cs index f83cada0f9..49feb5f8c8 100644 --- a/src/Umbraco.Core/Persistence/Factories/MemberTypeFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MemberTypeFactory.cs @@ -1,94 +1,94 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Rdbms; - -namespace Umbraco.Core.Persistence.Factories -{ - internal class MemberTypeFactory : IEntityFactory - { - private readonly Guid _nodeObjectType; - - public MemberTypeFactory(Guid nodeObjectType) - { - _nodeObjectType = nodeObjectType; - } - - public IMemberType BuildEntity(ContentTypeDto dto) - { - throw new System.NotImplementedException(); - } - - public ContentTypeDto BuildDto(IMemberType entity) - { - var contentTypeDto = new ContentTypeDto - { - Alias = entity.Alias, - Description = entity.Description, - Icon = entity.Icon, - Thumbnail = entity.Thumbnail, - NodeId = entity.Id, - AllowAtRoot = entity.AllowedAsRoot, - IsContainer = entity.IsContainer, - NodeDto = BuildNodeDto(entity) - }; - return contentTypeDto; - } - - public IEnumerable BuildMemberTypeDtos(IMemberType entity) - { - var memberType = entity as MemberType; - if (memberType == null || memberType.PropertyTypes.Any() == false) - return Enumerable.Empty(); - - var memberTypes = new List(); - foreach (var propertyType in memberType.PropertyTypes) - { - memberTypes.Add(new MemberTypeDto - { - NodeId = entity.Id, - PropertyTypeId = propertyType.Id, - CanEdit = memberType.MemberCanEditProperty(propertyType.Alias), - ViewOnProfile = memberType.MemberCanViewProperty(propertyType.Alias) - }); - } - - return memberTypes; - } - - private NodeDto BuildNodeDto(IMemberType entity) - { - var nodeDto = new NodeDto - { - CreateDate = entity.CreateDate, - NodeId = entity.Id, - Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)), - NodeObjectType = _nodeObjectType, - ParentId = entity.ParentId, - Path = entity.Path, - SortOrder = entity.SortOrder, - Text = entity.Name, - Trashed = false, - UniqueId = entity.Key, - UserId = entity.CreatorId - }; - return nodeDto; - } - - private int DeterminePropertyTypeId(int initialId, string alias, IEnumerable propertyTypes) - { - if (initialId == 0 || initialId == default(int)) - { - var propertyType = propertyTypes.SingleOrDefault(x => x.Alias.Equals(alias)); - if (propertyType == null) - return default(int); - - return propertyType.Id; - } - - return initialId; - } - } +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Factories +{ + internal class MemberTypeFactory : IEntityFactory + { + private readonly Guid _nodeObjectType; + + public MemberTypeFactory(Guid nodeObjectType) + { + _nodeObjectType = nodeObjectType; + } + + public IMemberType BuildEntity(ContentTypeDto dto) + { + throw new System.NotImplementedException(); + } + + public ContentTypeDto BuildDto(IMemberType entity) + { + var contentTypeDto = new ContentTypeDto + { + Alias = entity.Alias, + Description = entity.Description, + Icon = entity.Icon, + Thumbnail = entity.Thumbnail, + NodeId = entity.Id, + AllowAtRoot = entity.AllowedAsRoot, + IsContainer = entity.IsContainer, + NodeDto = BuildNodeDto(entity) + }; + return contentTypeDto; + } + + public IEnumerable BuildMemberTypeDtos(IMemberType entity) + { + var memberType = entity as MemberType; + if (memberType == null || memberType.PropertyTypes.Any() == false) + return Enumerable.Empty(); + + var memberTypes = new List(); + foreach (var propertyType in memberType.PropertyTypes) + { + memberTypes.Add(new MemberTypeDto + { + NodeId = entity.Id, + PropertyTypeId = propertyType.Id, + CanEdit = memberType.MemberCanEditProperty(propertyType.Alias), + ViewOnProfile = memberType.MemberCanViewProperty(propertyType.Alias) + }); + } + + return memberTypes; + } + + private NodeDto BuildNodeDto(IMemberType entity) + { + var nodeDto = new NodeDto + { + CreateDate = entity.CreateDate, + NodeId = entity.Id, + Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)), + NodeObjectType = _nodeObjectType, + ParentId = entity.ParentId, + Path = entity.Path, + SortOrder = entity.SortOrder, + Text = entity.Name, + Trashed = false, + UniqueId = entity.Key, + UserId = entity.CreatorId + }; + return nodeDto; + } + + private int DeterminePropertyTypeId(int initialId, string alias, IEnumerable propertyTypes) + { + if (initialId == 0 || initialId == default(int)) + { + var propertyType = propertyTypes.SingleOrDefault(x => x.Alias.Equals(alias)); + if (propertyType == null) + return default(int); + + return propertyType.Id; + } + + return initialId; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs index 95396cab89..5caa8db61a 100644 --- a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs @@ -1,157 +1,157 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Rdbms; - -namespace Umbraco.Core.Persistence.Factories -{ - internal class MemberTypeReadOnlyFactory : IEntityFactory - { - public IMemberType BuildEntity(MemberTypeReadOnlyDto dto) - { - var memberType = new MemberType(dto.ParentId) - { - Alias = dto.Alias, - AllowedAsRoot = dto.AllowAtRoot, - CreateDate = dto.CreateDate, - CreatorId = dto.UserId.HasValue ? dto.UserId.Value : 0, - Description = dto.Description, - Icon = dto.Icon, - Id = dto.NodeId, - IsContainer = dto.IsContainer, - Key = dto.UniqueId.Value, - Level = dto.Level, - Name = dto.Text, - Path = dto.Path, - SortOrder = dto.SortOrder, - Thumbnail = dto.Thumbnail, - Trashed = dto.Trashed, - UpdateDate = dto.CreateDate, - AllowedContentTypes = Enumerable.Empty() - }; - - var propertyTypeGroupCollection = GetPropertyTypeGroupCollection(dto, memberType); - memberType.PropertyGroups = propertyTypeGroupCollection; - - var propertyTypes = GetPropertyTypes(dto, memberType); - //By Convention we add 9 stnd PropertyTypes - This is only here to support loading of types that didn't have these conventions before. - var standardPropertyTypes = Constants.Conventions.Member.StandardPropertyTypeStubs; - foreach (var standardPropertyType in standardPropertyTypes) - { - if(dto.PropertyTypes.Any(x => x.Alias.Equals(standardPropertyType.Key))) continue; - - //Add the standard PropertyType to the current list - propertyTypes.Add(standardPropertyType.Value); - - //Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType - memberType.MemberTypePropertyTypes.Add(standardPropertyType.Key, - new Tuple(false, false, default(int))); - } - memberType.PropertyTypes = propertyTypes; - - return memberType; - } - - private PropertyGroupCollection GetPropertyTypeGroupCollection(MemberTypeReadOnlyDto dto, MemberType memberType) - { - var propertyGroups = new PropertyGroupCollection(); - foreach (var groupDto in dto.PropertyTypeGroups.Where(x => x.Id.HasValue)) - { - var group = new PropertyGroup(); - - //Only assign an Id if the PropertyGroup belongs to this ContentType - if (groupDto.ContentTypeNodeId == memberType.Id) - { - group.Id = groupDto.Id.Value; - - if (groupDto.ParentGroupId.HasValue) - group.ParentId = groupDto.ParentGroupId.Value; - } - else - { - //If the PropertyGroup is inherited, we add a reference to the group as a Parent. - group.ParentId = groupDto.Id; - } - - group.Name = groupDto.Text; - group.SortOrder = groupDto.SortOrder; - group.PropertyTypes = new PropertyTypeCollection(); - - //Because we are likely to have a group with no PropertyTypes we need to ensure that these are excluded - var localGroupDto = groupDto; - var typeDtos = dto.PropertyTypes.Where(x => x.Id.HasValue && x.Id > 0 && x.PropertyTypeGroupId.HasValue && x.PropertyTypeGroupId.Value == localGroupDto.Id.Value); - foreach (var typeDto in typeDtos) - { - //Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType - memberType.MemberTypePropertyTypes.Add(typeDto.Alias, - new Tuple(typeDto.CanEdit, typeDto.ViewOnProfile, typeDto.Id.Value)); - - var tempGroupDto = groupDto; - var propertyType = new PropertyType(typeDto.PropertyEditorAlias, - typeDto.DbType.EnumParse(true)) - { - Alias = typeDto.Alias, - DataTypeDefinitionId = typeDto.DataTypeId, - Description = typeDto.Description, - Id = typeDto.Id.Value, - Name = typeDto.Name, - HelpText = typeDto.HelpText, - Mandatory = typeDto.Mandatory, - SortOrder = typeDto.SortOrder, - ValidationRegExp = typeDto.ValidationRegExp, - PropertyGroupId = new Lazy(() => tempGroupDto.Id.Value), - CreateDate = memberType.CreateDate, - UpdateDate = memberType.UpdateDate - }; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - propertyType.ResetDirtyProperties(false); - group.PropertyTypes.Add(propertyType); - } - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - group.ResetDirtyProperties(false); - propertyGroups.Add(group); - } - - return propertyGroups; - } - - private List GetPropertyTypes(MemberTypeReadOnlyDto dto, MemberType memberType) - { - //Find PropertyTypes that does not belong to a PropertyTypeGroup - var propertyTypes = new List(); - foreach (var propertyType in dto.PropertyTypes.Where(x => (x.PropertyTypeGroupId.HasValue == false || x.PropertyTypeGroupId.Value == 0) && x.Id.HasValue)) - { - //Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType - memberType.MemberTypePropertyTypes.Add(propertyType.Alias, - new Tuple(propertyType.CanEdit, propertyType.ViewOnProfile, propertyType.Id.Value)); - //PropertyType Collection - propertyTypes.Add(new PropertyType(propertyType.PropertyEditorAlias, - propertyType.DbType.EnumParse(true)) - { - Alias = propertyType.Alias, - DataTypeDefinitionId = propertyType.DataTypeId, - Description = propertyType.Description, - HelpText = propertyType.HelpText, - Id = propertyType.Id.Value, - Mandatory = propertyType.Mandatory, - Name = propertyType.Name, - SortOrder = propertyType.SortOrder, - ValidationRegExp = propertyType.ValidationRegExp, - PropertyGroupId = new Lazy(() => default(int)), - CreateDate = dto.CreateDate, - UpdateDate = dto.CreateDate - }); - } - return propertyTypes; - } - - public MemberTypeReadOnlyDto BuildDto(IMemberType entity) - { - throw new System.NotImplementedException(); - } - - } +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Factories +{ + internal class MemberTypeReadOnlyFactory : IEntityFactory + { + public IMemberType BuildEntity(MemberTypeReadOnlyDto dto) + { + var memberType = new MemberType(dto.ParentId) + { + Alias = dto.Alias, + AllowedAsRoot = dto.AllowAtRoot, + CreateDate = dto.CreateDate, + CreatorId = dto.UserId.HasValue ? dto.UserId.Value : 0, + Description = dto.Description, + Icon = dto.Icon, + Id = dto.NodeId, + IsContainer = dto.IsContainer, + Key = dto.UniqueId.Value, + Level = dto.Level, + Name = dto.Text, + Path = dto.Path, + SortOrder = dto.SortOrder, + Thumbnail = dto.Thumbnail, + Trashed = dto.Trashed, + UpdateDate = dto.CreateDate, + AllowedContentTypes = Enumerable.Empty() + }; + + var propertyTypeGroupCollection = GetPropertyTypeGroupCollection(dto, memberType); + memberType.PropertyGroups = propertyTypeGroupCollection; + + var propertyTypes = GetPropertyTypes(dto, memberType); + //By Convention we add 9 stnd PropertyTypes - This is only here to support loading of types that didn't have these conventions before. + var standardPropertyTypes = Constants.Conventions.Member.StandardPropertyTypeStubs; + foreach (var standardPropertyType in standardPropertyTypes) + { + if(dto.PropertyTypes.Any(x => x.Alias.Equals(standardPropertyType.Key))) continue; + + //Add the standard PropertyType to the current list + propertyTypes.Add(standardPropertyType.Value); + + //Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType + memberType.MemberTypePropertyTypes.Add(standardPropertyType.Key, + new Tuple(false, false, default(int))); + } + memberType.PropertyTypes = propertyTypes; + + return memberType; + } + + private PropertyGroupCollection GetPropertyTypeGroupCollection(MemberTypeReadOnlyDto dto, MemberType memberType) + { + var propertyGroups = new PropertyGroupCollection(); + foreach (var groupDto in dto.PropertyTypeGroups.Where(x => x.Id.HasValue)) + { + var group = new PropertyGroup(); + + //Only assign an Id if the PropertyGroup belongs to this ContentType + if (groupDto.ContentTypeNodeId == memberType.Id) + { + group.Id = groupDto.Id.Value; + + if (groupDto.ParentGroupId.HasValue) + group.ParentId = groupDto.ParentGroupId.Value; + } + else + { + //If the PropertyGroup is inherited, we add a reference to the group as a Parent. + group.ParentId = groupDto.Id; + } + + group.Name = groupDto.Text; + group.SortOrder = groupDto.SortOrder; + group.PropertyTypes = new PropertyTypeCollection(); + + //Because we are likely to have a group with no PropertyTypes we need to ensure that these are excluded + var localGroupDto = groupDto; + var typeDtos = dto.PropertyTypes.Where(x => x.Id.HasValue && x.Id > 0 && x.PropertyTypeGroupId.HasValue && x.PropertyTypeGroupId.Value == localGroupDto.Id.Value); + foreach (var typeDto in typeDtos) + { + //Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType + memberType.MemberTypePropertyTypes.Add(typeDto.Alias, + new Tuple(typeDto.CanEdit, typeDto.ViewOnProfile, typeDto.Id.Value)); + + var tempGroupDto = groupDto; + var propertyType = new PropertyType(typeDto.PropertyEditorAlias, + typeDto.DbType.EnumParse(true)) + { + Alias = typeDto.Alias, + DataTypeDefinitionId = typeDto.DataTypeId, + Description = typeDto.Description, + Id = typeDto.Id.Value, + Name = typeDto.Name, + HelpText = typeDto.HelpText, + Mandatory = typeDto.Mandatory, + SortOrder = typeDto.SortOrder, + ValidationRegExp = typeDto.ValidationRegExp, + PropertyGroupId = new Lazy(() => tempGroupDto.Id.Value), + CreateDate = memberType.CreateDate, + UpdateDate = memberType.UpdateDate + }; + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + propertyType.ResetDirtyProperties(false); + group.PropertyTypes.Add(propertyType); + } + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + group.ResetDirtyProperties(false); + propertyGroups.Add(group); + } + + return propertyGroups; + } + + private List GetPropertyTypes(MemberTypeReadOnlyDto dto, MemberType memberType) + { + //Find PropertyTypes that does not belong to a PropertyTypeGroup + var propertyTypes = new List(); + foreach (var propertyType in dto.PropertyTypes.Where(x => (x.PropertyTypeGroupId.HasValue == false || x.PropertyTypeGroupId.Value == 0) && x.Id.HasValue)) + { + //Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType + memberType.MemberTypePropertyTypes.Add(propertyType.Alias, + new Tuple(propertyType.CanEdit, propertyType.ViewOnProfile, propertyType.Id.Value)); + //PropertyType Collection + propertyTypes.Add(new PropertyType(propertyType.PropertyEditorAlias, + propertyType.DbType.EnumParse(true)) + { + Alias = propertyType.Alias, + DataTypeDefinitionId = propertyType.DataTypeId, + Description = propertyType.Description, + HelpText = propertyType.HelpText, + Id = propertyType.Id.Value, + Mandatory = propertyType.Mandatory, + Name = propertyType.Name, + SortOrder = propertyType.SortOrder, + ValidationRegExp = propertyType.ValidationRegExp, + PropertyGroupId = new Lazy(() => default(int)), + CreateDate = dto.CreateDate, + UpdateDate = dto.CreateDate + }); + } + return propertyTypes; + } + + public MemberTypeReadOnlyDto BuildDto(IMemberType entity) + { + throw new System.NotImplementedException(); + } + + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Factories/TagFactory.cs b/src/Umbraco.Core/Persistence/Factories/TagFactory.cs index 7ad3414a7e..c154a60198 100644 --- a/src/Umbraco.Core/Persistence/Factories/TagFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/TagFactory.cs @@ -1,27 +1,27 @@ -using Umbraco.Core.Models; -using Umbraco.Core.Models.Rdbms; - -namespace Umbraco.Core.Persistence.Factories -{ - internal class TagFactory : IEntityFactory - { - public ITag BuildEntity(TagDto dto) - { - var model = new Tag(dto.Id, dto.Tag, dto.Group); - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - model.ResetDirtyProperties(false); - return model; - } - - public TagDto BuildDto(ITag entity) - { - return new TagDto - { - Id = entity.Id, - Group = entity.Group, - Tag = entity.Text - }; - } - } +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Factories +{ + internal class TagFactory : IEntityFactory + { + public ITag BuildEntity(TagDto dto) + { + var model = new Tag(dto.Id, dto.Tag, dto.Group); + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + model.ResetDirtyProperties(false); + return model; + } + + public TagDto BuildDto(ITag entity) + { + return new TagDto + { + Id = entity.Id, + Group = entity.Group, + Tag = entity.Text + }; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Mappers/MacroMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MacroMapper.cs index 46cc9cf869..05246ab59d 100644 --- a/src/Umbraco.Core/Persistence/Mappers/MacroMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/MacroMapper.cs @@ -1,45 +1,45 @@ -using System.Collections.Concurrent; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Rdbms; - -namespace Umbraco.Core.Persistence.Mappers -{ - [MapperFor(typeof(Macro))] - [MapperFor(typeof(IMacro))] - internal sealed class MacroMapper : BaseMapper - { - private static readonly ConcurrentDictionary PropertyInfoCacheInstance = new ConcurrentDictionary(); - - //NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it - // otherwise that would fail because there is no public constructor. - public MacroMapper() - { - BuildMap(); - } - - #region Overrides of BaseMapper - - internal override ConcurrentDictionary PropertyInfoCache - { - get { return PropertyInfoCacheInstance; } - } - - internal override void BuildMap() - { - CacheMap(src => src.Id, dto => dto.Id); - CacheMap(src => src.Alias, dto => dto.Alias); - CacheMap(src => src.CacheByPage, dto => dto.CacheByPage); - CacheMap(src => src.CacheByMember, dto => dto.CachePersonalized); - CacheMap(src => src.ControlAssembly, dto => dto.ScriptAssembly); - CacheMap(src => src.ControlType, dto => dto.ScriptType); - CacheMap(src => src.DontRender, dto => dto.DontRender); - CacheMap(src => src.Name, dto => dto.Name); - CacheMap(src => src.CacheDuration, dto => dto.RefreshRate); - CacheMap(src => src.ScriptPath, dto => dto.Python); - CacheMap(src => src.UseInEditor, dto => dto.UseInEditor); - CacheMap(src => src.XsltPath, dto => dto.Xslt); - } - - #endregion - } +using System.Collections.Concurrent; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Mappers +{ + [MapperFor(typeof(Macro))] + [MapperFor(typeof(IMacro))] + internal sealed class MacroMapper : BaseMapper + { + private static readonly ConcurrentDictionary PropertyInfoCacheInstance = new ConcurrentDictionary(); + + //NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it + // otherwise that would fail because there is no public constructor. + public MacroMapper() + { + BuildMap(); + } + + #region Overrides of BaseMapper + + internal override ConcurrentDictionary PropertyInfoCache + { + get { return PropertyInfoCacheInstance; } + } + + internal override void BuildMap() + { + CacheMap(src => src.Id, dto => dto.Id); + CacheMap(src => src.Alias, dto => dto.Alias); + CacheMap(src => src.CacheByPage, dto => dto.CacheByPage); + CacheMap(src => src.CacheByMember, dto => dto.CachePersonalized); + CacheMap(src => src.ControlAssembly, dto => dto.ScriptAssembly); + CacheMap(src => src.ControlType, dto => dto.ScriptType); + CacheMap(src => src.DontRender, dto => dto.DontRender); + CacheMap(src => src.Name, dto => dto.Name); + CacheMap(src => src.CacheDuration, dto => dto.RefreshRate); + CacheMap(src => src.ScriptPath, dto => dto.Python); + CacheMap(src => src.UseInEditor, dto => dto.UseInEditor); + CacheMap(src => src.XsltPath, dto => dto.Xslt); + } + + #endregion + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs index d895e1c127..d2bbeb0792 100644 --- a/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs @@ -1,74 +1,74 @@ -using System.Collections.Concurrent; -using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Rdbms; - -namespace Umbraco.Core.Persistence.Mappers -{ - /// - /// Represents a to DTO mapper used to translate the properties of the public api - /// implementation to that of the database's DTO as sql: [tableName].[columnName]. - /// - [MapperFor(typeof(IMember))] - [MapperFor(typeof(Member))] - public sealed class MemberMapper : BaseMapper - { - private static readonly ConcurrentDictionary PropertyInfoCacheInstance = new ConcurrentDictionary(); - - //NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it - // otherwise that would fail because there is no public constructor. - public MemberMapper() - { - BuildMap(); - } - - #region Overrides of BaseMapper - - internal override ConcurrentDictionary PropertyInfoCache - { - get { return PropertyInfoCacheInstance; } - } - - internal override void BuildMap() - { - CacheMap(src => src.Id, dto => dto.NodeId); - CacheMap(src => src.CreateDate, dto => dto.CreateDate); - CacheMap(src => ((IUmbracoEntity)src).Level, dto => dto.Level); - CacheMap(src => ((IUmbracoEntity)src).ParentId, dto => dto.ParentId); - CacheMap(src => ((IUmbracoEntity)src).Path, dto => dto.Path); - CacheMap(src => ((IUmbracoEntity)src).SortOrder, dto => dto.SortOrder); - CacheMap(src => ((IUmbracoEntity)src).CreatorId, dto => dto.UserId); - CacheMap(src => src.Name, dto => dto.Text); - CacheMap(src => src.Trashed, dto => dto.Trashed); - CacheMap(src => src.Key, dto => dto.UniqueId); - CacheMap(src => src.ContentTypeId, dto => dto.ContentTypeId); - CacheMap(src => src.ContentTypeAlias, dto => dto.Alias); - CacheMap(src => src.UpdateDate, dto => dto.VersionDate); - CacheMap(src => src.Version, dto => dto.VersionId); - - CacheMap(src => src.Email, dto => dto.Email); - CacheMap(src => src.Username, dto => dto.LoginName); - CacheMap(src => src.Password, dto => dto.Password); - - CacheMap(src => src.IsApproved, dto => dto.Integer); - CacheMap(src => src.IsLockedOut, dto => dto.Integer); - CacheMap(src => src.Comments, dto => dto.Text); - CacheMap(src => src.PasswordAnswer, dto => dto.VarChar); - CacheMap(src => src.PasswordQuestion, dto => dto.VarChar); - CacheMap(src => src.FailedPasswordAttempts, dto => dto.Integer); - CacheMap(src => src.LastLockoutDate, dto => dto.Date); - CacheMap(src => src.LastLoginDate, dto => dto.Date); - CacheMap(src => src.LastPasswordChangeDate, dto => dto.Date); - - /* Internal experiment */ - CacheMap(src => src.DateTimePropertyValue, dto => dto.Date); - CacheMap(src => src.IntegerropertyValue, dto => dto.Integer); - CacheMap(src => src.BoolPropertyValue, dto => dto.Integer); - CacheMap(src => src.LongStringPropertyValue, dto => dto.Text); - CacheMap(src => src.ShortStringPropertyValue, dto => dto.VarChar); - CacheMap(src => src.PropertyTypeAlias, dto => dto.Alias); - } - - #endregion - } +using System.Collections.Concurrent; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Mappers +{ + /// + /// Represents a to DTO mapper used to translate the properties of the public api + /// implementation to that of the database's DTO as sql: [tableName].[columnName]. + /// + [MapperFor(typeof(IMember))] + [MapperFor(typeof(Member))] + public sealed class MemberMapper : BaseMapper + { + private static readonly ConcurrentDictionary PropertyInfoCacheInstance = new ConcurrentDictionary(); + + //NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it + // otherwise that would fail because there is no public constructor. + public MemberMapper() + { + BuildMap(); + } + + #region Overrides of BaseMapper + + internal override ConcurrentDictionary PropertyInfoCache + { + get { return PropertyInfoCacheInstance; } + } + + internal override void BuildMap() + { + CacheMap(src => src.Id, dto => dto.NodeId); + CacheMap(src => src.CreateDate, dto => dto.CreateDate); + CacheMap(src => ((IUmbracoEntity)src).Level, dto => dto.Level); + CacheMap(src => ((IUmbracoEntity)src).ParentId, dto => dto.ParentId); + CacheMap(src => ((IUmbracoEntity)src).Path, dto => dto.Path); + CacheMap(src => ((IUmbracoEntity)src).SortOrder, dto => dto.SortOrder); + CacheMap(src => ((IUmbracoEntity)src).CreatorId, dto => dto.UserId); + CacheMap(src => src.Name, dto => dto.Text); + CacheMap(src => src.Trashed, dto => dto.Trashed); + CacheMap(src => src.Key, dto => dto.UniqueId); + CacheMap(src => src.ContentTypeId, dto => dto.ContentTypeId); + CacheMap(src => src.ContentTypeAlias, dto => dto.Alias); + CacheMap(src => src.UpdateDate, dto => dto.VersionDate); + CacheMap(src => src.Version, dto => dto.VersionId); + + CacheMap(src => src.Email, dto => dto.Email); + CacheMap(src => src.Username, dto => dto.LoginName); + CacheMap(src => src.Password, dto => dto.Password); + + CacheMap(src => src.IsApproved, dto => dto.Integer); + CacheMap(src => src.IsLockedOut, dto => dto.Integer); + CacheMap(src => src.Comments, dto => dto.Text); + CacheMap(src => src.PasswordAnswer, dto => dto.VarChar); + CacheMap(src => src.PasswordQuestion, dto => dto.VarChar); + CacheMap(src => src.FailedPasswordAttempts, dto => dto.Integer); + CacheMap(src => src.LastLockoutDate, dto => dto.Date); + CacheMap(src => src.LastLoginDate, dto => dto.Date); + CacheMap(src => src.LastPasswordChangeDate, dto => dto.Date); + + /* Internal experiment */ + CacheMap(src => src.DateTimePropertyValue, dto => dto.Date); + CacheMap(src => src.IntegerropertyValue, dto => dto.Integer); + CacheMap(src => src.BoolPropertyValue, dto => dto.Integer); + CacheMap(src => src.LongStringPropertyValue, dto => dto.Text); + CacheMap(src => src.ShortStringPropertyValue, dto => dto.VarChar); + CacheMap(src => src.PropertyTypeAlias, dto => dto.Alias); + } + + #endregion + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Mappers/MemberTypeMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MemberTypeMapper.cs index eafc1e5031..69a5190b46 100644 --- a/src/Umbraco.Core/Persistence/Mappers/MemberTypeMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/MemberTypeMapper.cs @@ -1,57 +1,57 @@ -using System.Collections.Concurrent; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Rdbms; - -namespace Umbraco.Core.Persistence.Mappers -{ - /// - /// Represents a to DTO mapper used to translate the properties of the public api - /// implementation to that of the database's DTO as sql: [tableName].[columnName]. - /// - [MapperFor(typeof (MemberType))] - [MapperFor(typeof (IMemberType))] - public sealed class MemberTypeMapper : BaseMapper - { - private static readonly ConcurrentDictionary PropertyInfoCacheInstance = - new ConcurrentDictionary(); - - //NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it - // otherwise that would fail because there is no public constructor. - public MemberTypeMapper() - { - BuildMap(); - } - - #region Overrides of BaseMapper - - internal override ConcurrentDictionary PropertyInfoCache - { - get { return PropertyInfoCacheInstance; } - } - - internal override void BuildMap() - { - if (PropertyInfoCache.IsEmpty) - { - CacheMap(src => src.Id, dto => dto.NodeId); - CacheMap(src => src.CreateDate, dto => dto.CreateDate); - CacheMap(src => src.Level, dto => dto.Level); - CacheMap(src => src.ParentId, dto => dto.ParentId); - CacheMap(src => src.Path, dto => dto.Path); - CacheMap(src => src.SortOrder, dto => dto.SortOrder); - CacheMap(src => src.Name, dto => dto.Text); - CacheMap(src => src.Trashed, dto => dto.Trashed); - CacheMap(src => src.Key, dto => dto.UniqueId); - CacheMap(src => src.CreatorId, dto => dto.UserId); - CacheMap(src => src.Alias, dto => dto.Alias); - CacheMap(src => src.AllowedAsRoot, dto => dto.AllowAtRoot); - CacheMap(src => src.Description, dto => dto.Description); - CacheMap(src => src.Icon, dto => dto.Icon); - CacheMap(src => src.IsContainer, dto => dto.IsContainer); - CacheMap(src => src.Thumbnail, dto => dto.Thumbnail); - } - } - - #endregion - } +using System.Collections.Concurrent; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Mappers +{ + /// + /// Represents a to DTO mapper used to translate the properties of the public api + /// implementation to that of the database's DTO as sql: [tableName].[columnName]. + /// + [MapperFor(typeof (MemberType))] + [MapperFor(typeof (IMemberType))] + public sealed class MemberTypeMapper : BaseMapper + { + private static readonly ConcurrentDictionary PropertyInfoCacheInstance = + new ConcurrentDictionary(); + + //NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it + // otherwise that would fail because there is no public constructor. + public MemberTypeMapper() + { + BuildMap(); + } + + #region Overrides of BaseMapper + + internal override ConcurrentDictionary PropertyInfoCache + { + get { return PropertyInfoCacheInstance; } + } + + internal override void BuildMap() + { + if (PropertyInfoCache.IsEmpty) + { + CacheMap(src => src.Id, dto => dto.NodeId); + CacheMap(src => src.CreateDate, dto => dto.CreateDate); + CacheMap(src => src.Level, dto => dto.Level); + CacheMap(src => src.ParentId, dto => dto.ParentId); + CacheMap(src => src.Path, dto => dto.Path); + CacheMap(src => src.SortOrder, dto => dto.SortOrder); + CacheMap(src => src.Name, dto => dto.Text); + CacheMap(src => src.Trashed, dto => dto.Trashed); + CacheMap(src => src.Key, dto => dto.UniqueId); + CacheMap(src => src.CreatorId, dto => dto.UserId); + CacheMap(src => src.Alias, dto => dto.Alias); + CacheMap(src => src.AllowedAsRoot, dto => dto.AllowAtRoot); + CacheMap(src => src.Description, dto => dto.Description); + CacheMap(src => src.Icon, dto => dto.Icon); + CacheMap(src => src.IsContainer, dto => dto.IsContainer); + CacheMap(src => src.Thumbnail, dto => dto.Thumbnail); + } + } + + #endregion + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroPropertyTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroPropertyTable.cs index 7be3715259..7ba5224e5a 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroPropertyTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroPropertyTable.cs @@ -1,26 +1,26 @@ -using System; -using Umbraco.Core.Configuration; - -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven -{ - /// - /// Creats a unique index across two columns so we cannot have duplicate property aliases for one macro - /// - [Migration("7.0.0", 5, GlobalSettings.UmbracoMigrationName)] - public class AddIndexToCmsMacroPropertyTable : MigrationBase - { - public override void Up() - { - Create.Index("IX_cmsMacroProperty_Alias").OnTable("cmsMacroProperty") - .OnColumn("macro") - .Ascending() - .OnColumn("macroPropertyAlias") - .Unique(); - } - - public override void Down() - { - throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); - } - } +using System; +using Umbraco.Core.Configuration; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven +{ + /// + /// Creats a unique index across two columns so we cannot have duplicate property aliases for one macro + /// + [Migration("7.0.0", 5, GlobalSettings.UmbracoMigrationName)] + public class AddIndexToCmsMacroPropertyTable : MigrationBase + { + public override void Up() + { + Create.Index("IX_cmsMacroProperty_Alias").OnTable("cmsMacroProperty") + .OnColumn("macro") + .Ascending() + .OnColumn("macroPropertyAlias") + .Unique(); + } + + public override void Down() + { + throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs index e4066d70ab..3b4f1f8817 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs @@ -1,22 +1,22 @@ -using System; -using Umbraco.Core.Configuration; - -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven -{ - /// - /// Creates a unique index on the macro alias so we cannot have duplicates by alias - /// - [Migration("7.0.0", 4, GlobalSettings.UmbracoMigrationName)] - public class AddIndexToCmsMacroTable : MigrationBase - { - public override void Up() - { - Create.Index("IX_cmsMacro_Alias").OnTable("cmsMacro").OnColumn("macroAlias").Unique(); - } - - public override void Down() - { - throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); - } - } +using System; +using Umbraco.Core.Configuration; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven +{ + /// + /// Creates a unique index on the macro alias so we cannot have duplicates by alias + /// + [Migration("7.0.0", 4, GlobalSettings.UmbracoMigrationName)] + public class AddIndexToCmsMacroTable : MigrationBase + { + public override void Up() + { + Create.Index("IX_cmsMacro_Alias").OnTable("cmsMacro").OnColumn("macroAlias").Unique(); + } + + public override void Down() + { + throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddPropertyEditorAliasColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddPropertyEditorAliasColumn.cs index f52b09b990..93bb3e4193 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddPropertyEditorAliasColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddPropertyEditorAliasColumn.cs @@ -1,20 +1,20 @@ -using System; -using Umbraco.Core.Configuration; - -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven -{ - - [Migration("7.0.0", 0, GlobalSettings.UmbracoMigrationName)] - public class AddPropertyEditorAliasColumn : MigrationBase - { - public override void Up() - { - Alter.Table("cmsDataType").AddColumn("propertyEditorAlias").AsString(255).NotNullable().WithDefaultValue(""); - } - - public override void Down() - { - throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); - } - } +using System; +using Umbraco.Core.Configuration; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven +{ + + [Migration("7.0.0", 0, GlobalSettings.UmbracoMigrationName)] + public class AddPropertyEditorAliasColumn : MigrationBase + { + public override void Up() + { + Alter.Table("cmsDataType").AddColumn("propertyEditorAlias").AsString(255).NotNullable().WithDefaultValue(""); + } + + public override void Down() + { + throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs index c010305562..2ab9673fdc 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs @@ -1,58 +1,58 @@ -using System; -using Umbraco.Core.Configuration; -using Umbraco.Core.PropertyEditors; - -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven -{ - /// - /// We are removing the cmsMacroPropertyType which the cmsMacroProperty references and the cmsMacroProperty.macroPropertyType column - /// needs to be changed to editorAlias, we'll do this by removing the constraint,changing the macroPropertyType to the new - /// editorAlias column (and maintaing data so we can reference it) - /// - [Migration("7.0.0", 6, GlobalSettings.UmbracoMigrationName)] - public class AlterCmsMacroPropertyTable : MigrationBase - { - public override void Up() - { - //now that the controlId column is renamed and now a string we need to convert - if (Context == null || Context.Database == null) return; - - //"DF_cmsMacroProperty_macroPropertyHidden"" - Delete.DefaultConstraint().OnTable("cmsMacroProperty").OnColumn("macroPropertyHidden"); - - Delete.Column("macroPropertyHidden").FromTable("cmsMacroProperty"); - - Delete.ForeignKey("FK_cmsMacroProperty_cmsMacroPropertyType_id").OnTable("cmsMacroProperty"); - - Alter.Table("cmsMacroProperty").AddColumn("editorAlias").AsString(255).NotNullable().WithDefaultValue(""); - - //we need to get the data and create the migration scripts before we change the actual schema bits below! - var list = Context.Database.Fetch("SELECT * FROM cmsMacroPropertyType"); - foreach (var item in list) - { - - var alias = item.macroPropertyTypeAlias; - //check if there's a map created - var newAlias = (string)LegacyParameterEditorAliasConverter.GetNewAliasFromLegacyAlias(alias); - if (newAlias.IsNullOrWhiteSpace() == false) - { - alias = newAlias; - } - - //update the table with the alias, the current macroPropertyType will contain the original id - Update.Table("cmsMacroProperty").Set(new { editorAlias = alias }).Where(new { macroPropertyType = item.id }); - } - - //drop the column now - Delete.Column("macroPropertyType").FromTable("cmsMacroProperty"); - - //drop the default constraing - Delete.DefaultConstraint().OnTable("cmsMacroProperty").OnColumn("editorAlias"); - } - - public override void Down() - { - throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); - } - } +using System; +using Umbraco.Core.Configuration; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven +{ + /// + /// We are removing the cmsMacroPropertyType which the cmsMacroProperty references and the cmsMacroProperty.macroPropertyType column + /// needs to be changed to editorAlias, we'll do this by removing the constraint,changing the macroPropertyType to the new + /// editorAlias column (and maintaing data so we can reference it) + /// + [Migration("7.0.0", 6, GlobalSettings.UmbracoMigrationName)] + public class AlterCmsMacroPropertyTable : MigrationBase + { + public override void Up() + { + //now that the controlId column is renamed and now a string we need to convert + if (Context == null || Context.Database == null) return; + + //"DF_cmsMacroProperty_macroPropertyHidden"" + Delete.DefaultConstraint().OnTable("cmsMacroProperty").OnColumn("macroPropertyHidden"); + + Delete.Column("macroPropertyHidden").FromTable("cmsMacroProperty"); + + Delete.ForeignKey("FK_cmsMacroProperty_cmsMacroPropertyType_id").OnTable("cmsMacroProperty"); + + Alter.Table("cmsMacroProperty").AddColumn("editorAlias").AsString(255).NotNullable().WithDefaultValue(""); + + //we need to get the data and create the migration scripts before we change the actual schema bits below! + var list = Context.Database.Fetch("SELECT * FROM cmsMacroPropertyType"); + foreach (var item in list) + { + + var alias = item.macroPropertyTypeAlias; + //check if there's a map created + var newAlias = (string)LegacyParameterEditorAliasConverter.GetNewAliasFromLegacyAlias(alias); + if (newAlias.IsNullOrWhiteSpace() == false) + { + alias = newAlias; + } + + //update the table with the alias, the current macroPropertyType will contain the original id + Update.Table("cmsMacroProperty").Set(new { editorAlias = alias }).Where(new { macroPropertyType = item.id }); + } + + //drop the column now + Delete.Column("macroPropertyType").FromTable("cmsMacroProperty"); + + //drop the default constraing + Delete.DefaultConstraint().OnTable("cmsMacroProperty").OnColumn("editorAlias"); + } + + public override void Down() + { + throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs index 35b28c577d..7b23e22504 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs @@ -1,130 +1,130 @@ -using System; -using System.Data; -using System.Linq; -using Umbraco.Core.Configuration; -using Umbraco.Core.Logging; -using Umbraco.Core.Models.Rdbms; - -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven -{ - [Migration("7.0.0", 8, GlobalSettings.UmbracoMigrationName)] - public class AlterTagRelationsTable : MigrationBase - { - public override void Up() - { - if (Context == null || Context.Database == null) return; - - Initial(); - - Upgrade(); - - Final(); - } - - private void Initial() - { - //create a new col which we will make a foreign key, but first needs to be populated with data. - Alter.Table("cmsTagRelationship").AddColumn("propertyTypeId").AsInt32().Nullable(); - - //we need to drop the primary key - Delete.PrimaryKey("PK_cmsTagRelationship").FromTable("cmsTagRelationship"); - - //drop the foreign key on umbracoNode - Delete.ForeignKey("FK_cmsTagRelationship_umbracoNode_id").OnTable("cmsTagRelationship"); - } - - private void Upgrade() - { - //get all data from the tag relationship table - var tagRelations = Context.Database.Fetch("SELECT nodeId, tagId FROM cmsTagRelationship"); - - //get all node id -> property type id references for nodes that are in a tag relations and for properties that are of those nodes that are of the tag data type - var propertyTypeIdRef = Context.Database.Fetch(@"SELECT DISTINCT cmsTagRelationship.nodeId as NodeId, cmsPropertyType.id as PropertyTypeId - FROM cmsTags - INNER JOIN cmsTagRelationship ON cmsTagRelationship.tagId = cmsTags.id - INNER JOIN umbracoNode ON umbracoNode.id = cmsTagRelationship.nodeId - INNER JOIN cmsContent ON cmsContent.nodeId = umbracoNode.id - INNER JOIN cmsContentType ON cmsContentType.nodeId = cmsContent.contentType - INNER JOIN cmsPropertyType ON cmsPropertyType.contentTypeId = cmsContentType.nodeId - INNER JOIN cmsDataType ON cmsDataType.nodeId = cmsPropertyType.dataTypeId - WHERE cmsDataType.controlId = '4023E540-92F5-11DD-AD8B-0800200C9A66'"); - - foreach (var tr in tagRelations) - { - //for each tag relation we need to assign it a property type id which must exist in our references, if it doesn't it means that - // someone has tag data that relates to node that is not in the cmsContent table - we'll have to delete it and log it if that is the case. - - var propertyTypes = propertyTypeIdRef.Where(x => x.NodeId == tr.NodeId).ToArray(); - if (propertyTypes.Length == 0) - { - LogHelper.Warn("There was no cmsContent reference for cmsTagRelationship for nodeId " - + tr.NodeId + - ". The new tag system only supports tags with references to content in the cmsContent and cmsPropertyType tables. This row will be deleted: " - + string.Format("nodeId: {0}, tagId: {1}", tr.NodeId, tr.TagId)); - Delete.FromTable("cmsTagRelationship").Row(new { nodeId = tr.NodeId, tagId = tr.TagId }); - } - else - { - //update the first one found to the existing row, there might be more if there are more than one tag property assigned to the node - //in that case we need to create a row. - - //update the table with the alias, the current editorAlias will contain the original id - var first = propertyTypes[0]; - Update.Table("cmsTagRelationship") - .Set(new { propertyTypeId = first.PropertyTypeId }) - .Where(new { nodeId = tr.NodeId, tagId = tr.TagId }); - - if (propertyTypes.Length > 1) - { - //now we need to create rows for the other ones - for (var i = 1; i < propertyTypes.Length; i++) - { - Insert.IntoTable("cmsTagRelationship").Row(new { nodeId = tr.NodeId, tagId = tr.TagId, propertyTypeId = propertyTypes[i].PropertyTypeId }); - } - } - } - } - } - - private void Final() - { - //we need to change this to not nullable - Alter.Table("cmsTagRelationship").AlterColumn("propertyTypeId").AsInt32().NotNullable(); - - //we need to re-add the new primary key on all 3 columns - Create.PrimaryKey("PK_cmsTagRelationship").OnTable("cmsTagRelationship").Columns(new[] {"nodeId", "propertyTypeId", "tagId"}); - - //now we need to add a foreign key to the propertyTypeId column and change it's constraints - Create.ForeignKey("FK_cmsTagRelationship_cmsPropertyType") - .FromTable("cmsTagRelationship") - .ForeignColumn("propertyTypeId") - .ToTable("cmsPropertyType") - .PrimaryColumn("id") - .OnDelete(Rule.None) - .OnUpdate(Rule.None); - - //now we need to add a foreign key to the nodeId column to cmsContent (intead of the original umbracoNode) - Create.ForeignKey("FK_cmsTagRelationship_cmsContent") - .FromTable("cmsTagRelationship") - .ForeignColumn("nodeId") - .ToTable("cmsContent") - .PrimaryColumn("nodeId") - .OnDelete(Rule.None) - .OnUpdate(Rule.None); - } - - public override void Down() - { - throw new NotSupportedException(); - } - - /// - /// A custom class to map to so that we can linq to it easily without dynamics - /// - private class PropertyTypeReferenceDto - { - public int NodeId { get; set; } - public int PropertyTypeId { get; set; } - } - } +using System; +using System.Data; +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven +{ + [Migration("7.0.0", 8, GlobalSettings.UmbracoMigrationName)] + public class AlterTagRelationsTable : MigrationBase + { + public override void Up() + { + if (Context == null || Context.Database == null) return; + + Initial(); + + Upgrade(); + + Final(); + } + + private void Initial() + { + //create a new col which we will make a foreign key, but first needs to be populated with data. + Alter.Table("cmsTagRelationship").AddColumn("propertyTypeId").AsInt32().Nullable(); + + //we need to drop the primary key + Delete.PrimaryKey("PK_cmsTagRelationship").FromTable("cmsTagRelationship"); + + //drop the foreign key on umbracoNode + Delete.ForeignKey("FK_cmsTagRelationship_umbracoNode_id").OnTable("cmsTagRelationship"); + } + + private void Upgrade() + { + //get all data from the tag relationship table + var tagRelations = Context.Database.Fetch("SELECT nodeId, tagId FROM cmsTagRelationship"); + + //get all node id -> property type id references for nodes that are in a tag relations and for properties that are of those nodes that are of the tag data type + var propertyTypeIdRef = Context.Database.Fetch(@"SELECT DISTINCT cmsTagRelationship.nodeId as NodeId, cmsPropertyType.id as PropertyTypeId + FROM cmsTags + INNER JOIN cmsTagRelationship ON cmsTagRelationship.tagId = cmsTags.id + INNER JOIN umbracoNode ON umbracoNode.id = cmsTagRelationship.nodeId + INNER JOIN cmsContent ON cmsContent.nodeId = umbracoNode.id + INNER JOIN cmsContentType ON cmsContentType.nodeId = cmsContent.contentType + INNER JOIN cmsPropertyType ON cmsPropertyType.contentTypeId = cmsContentType.nodeId + INNER JOIN cmsDataType ON cmsDataType.nodeId = cmsPropertyType.dataTypeId + WHERE cmsDataType.controlId = '4023E540-92F5-11DD-AD8B-0800200C9A66'"); + + foreach (var tr in tagRelations) + { + //for each tag relation we need to assign it a property type id which must exist in our references, if it doesn't it means that + // someone has tag data that relates to node that is not in the cmsContent table - we'll have to delete it and log it if that is the case. + + var propertyTypes = propertyTypeIdRef.Where(x => x.NodeId == tr.NodeId).ToArray(); + if (propertyTypes.Length == 0) + { + LogHelper.Warn("There was no cmsContent reference for cmsTagRelationship for nodeId " + + tr.NodeId + + ". The new tag system only supports tags with references to content in the cmsContent and cmsPropertyType tables. This row will be deleted: " + + string.Format("nodeId: {0}, tagId: {1}", tr.NodeId, tr.TagId)); + Delete.FromTable("cmsTagRelationship").Row(new { nodeId = tr.NodeId, tagId = tr.TagId }); + } + else + { + //update the first one found to the existing row, there might be more if there are more than one tag property assigned to the node + //in that case we need to create a row. + + //update the table with the alias, the current editorAlias will contain the original id + var first = propertyTypes[0]; + Update.Table("cmsTagRelationship") + .Set(new { propertyTypeId = first.PropertyTypeId }) + .Where(new { nodeId = tr.NodeId, tagId = tr.TagId }); + + if (propertyTypes.Length > 1) + { + //now we need to create rows for the other ones + for (var i = 1; i < propertyTypes.Length; i++) + { + Insert.IntoTable("cmsTagRelationship").Row(new { nodeId = tr.NodeId, tagId = tr.TagId, propertyTypeId = propertyTypes[i].PropertyTypeId }); + } + } + } + } + } + + private void Final() + { + //we need to change this to not nullable + Alter.Table("cmsTagRelationship").AlterColumn("propertyTypeId").AsInt32().NotNullable(); + + //we need to re-add the new primary key on all 3 columns + Create.PrimaryKey("PK_cmsTagRelationship").OnTable("cmsTagRelationship").Columns(new[] {"nodeId", "propertyTypeId", "tagId"}); + + //now we need to add a foreign key to the propertyTypeId column and change it's constraints + Create.ForeignKey("FK_cmsTagRelationship_cmsPropertyType") + .FromTable("cmsTagRelationship") + .ForeignColumn("propertyTypeId") + .ToTable("cmsPropertyType") + .PrimaryColumn("id") + .OnDelete(Rule.None) + .OnUpdate(Rule.None); + + //now we need to add a foreign key to the nodeId column to cmsContent (intead of the original umbracoNode) + Create.ForeignKey("FK_cmsTagRelationship_cmsContent") + .FromTable("cmsTagRelationship") + .ForeignColumn("nodeId") + .ToTable("cmsContent") + .PrimaryColumn("nodeId") + .OnDelete(Rule.None) + .OnUpdate(Rule.None); + } + + public override void Down() + { + throw new NotSupportedException(); + } + + /// + /// A custom class to map to so that we can linq to it easily without dynamics + /// + private class PropertyTypeReferenceDto + { + public int NodeId { get; set; } + public int PropertyTypeId { get; set; } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs index 61747d03c7..581fd7ef07 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs @@ -1,30 +1,30 @@ -using System; -using System.Data; -using Umbraco.Core.Configuration; - -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven -{ - [Migration("7.0.0", 9, GlobalSettings.UmbracoMigrationName)] - public class AlterTagsTable : MigrationBase - { - public override void Up() - { - //add a foreign key to the parent id column too! - Create.ForeignKey("FK_cmsTags_cmsTags") - .FromTable("cmsTags") - .ForeignColumn("ParentId") - .ToTable("cmsTags") - .PrimaryColumn("id") - .OnDelete(Rule.None) - .OnUpdate(Rule.None); - - //add an index to tag/group since it's queried often - Create.Index("IX_cmsTags").OnTable("cmsTags").OnColumn("tag").Ascending().OnColumn("group").Ascending().WithOptions().NonClustered(); - } - - public override void Down() - { - throw new NotImplementedException(); - } - } +using System; +using System.Data; +using Umbraco.Core.Configuration; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven +{ + [Migration("7.0.0", 9, GlobalSettings.UmbracoMigrationName)] + public class AlterTagsTable : MigrationBase + { + public override void Up() + { + //add a foreign key to the parent id column too! + Create.ForeignKey("FK_cmsTags_cmsTags") + .FromTable("cmsTags") + .ForeignColumn("ParentId") + .ToTable("cmsTags") + .PrimaryColumn("id") + .OnDelete(Rule.None) + .OnUpdate(Rule.None); + + //add an index to tag/group since it's queried often + Create.Index("IX_cmsTags").OnTable("cmsTags").OnColumn("tag").Ascending().OnColumn("group").Ascending().WithOptions().NonClustered(); + } + + public override void Down() + { + throw new NotImplementedException(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterUserTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterUserTable.cs index d7f1cc384a..4c2acdd5ae 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterUserTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterUserTable.cs @@ -1,23 +1,23 @@ -using System; -using Umbraco.Core.Configuration; - -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven -{ - [Migration("7.0.0", 3, GlobalSettings.UmbracoMigrationName)] - public class AlterUserTable : MigrationBase - { - public override void Up() - { - Delete.Column("userDefaultPermissions").FromTable("umbracoUser"); - - //"[DF_umbracoUser_defaultToLiveEditing]"" - Delete.DefaultConstraint().OnTable("umbracoUser").OnColumn("defaultToLiveEditing"); - Delete.Column("defaultToLiveEditing").FromTable("umbracoUser"); - } - - public override void Down() - { - throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); - } - } +using System; +using Umbraco.Core.Configuration; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven +{ + [Migration("7.0.0", 3, GlobalSettings.UmbracoMigrationName)] + public class AlterUserTable : MigrationBase + { + public override void Up() + { + Delete.Column("userDefaultPermissions").FromTable("umbracoUser"); + + //"[DF_umbracoUser_defaultToLiveEditing]"" + Delete.DefaultConstraint().OnTable("umbracoUser").OnColumn("defaultToLiveEditing"); + Delete.Column("defaultToLiveEditing").FromTable("umbracoUser"); + } + + public override void Down() + { + throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/DropControlIdColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/DropControlIdColumn.cs index 6208164ea7..df3a4f0450 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/DropControlIdColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/DropControlIdColumn.cs @@ -1,21 +1,21 @@ -using System; -using Umbraco.Core.Configuration; - -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven -{ - [Migration("7.0.0", 2, GlobalSettings.UmbracoMigrationName)] - public class DropControlIdColumn : MigrationBase - { - public override void Up() - { - Delete.Column("controlId").FromTable("cmsDataType"); - //drop the default contstraint on the new column too - Delete.DefaultConstraint().OnTable("cmsDataType").OnColumn("propertyEditorAlias"); - } - - public override void Down() - { - throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); - } - } +using System; +using Umbraco.Core.Configuration; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven +{ + [Migration("7.0.0", 2, GlobalSettings.UmbracoMigrationName)] + public class DropControlIdColumn : MigrationBase + { + public override void Up() + { + Delete.Column("controlId").FromTable("cmsDataType"); + //drop the default contstraint on the new column too + Delete.DefaultConstraint().OnTable("cmsDataType").OnColumn("propertyEditorAlias"); + } + + public override void Down() + { + throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/RemoveCmsMacroPropertyTypeTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/RemoveCmsMacroPropertyTypeTable.cs index 5405724458..c7adc2af89 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/RemoveCmsMacroPropertyTypeTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/RemoveCmsMacroPropertyTypeTable.cs @@ -1,19 +1,19 @@ -using System; -using Umbraco.Core.Configuration; - -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven -{ - [Migration("7.0.0", 7, GlobalSettings.UmbracoMigrationName)] - public class RemoveCmsMacroPropertyTypeTable : MigrationBase - { - public override void Up() - { - Delete.Table("cmsMacroPropertyType"); - } - - public override void Down() - { - throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); - } - } +using System; +using Umbraco.Core.Configuration; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven +{ + [Migration("7.0.0", 7, GlobalSettings.UmbracoMigrationName)] + public class RemoveCmsMacroPropertyTypeTable : MigrationBase + { + public override void Up() + { + Delete.Table("cmsMacroPropertyType"); + } + + public override void Down() + { + throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateControlIdToPropertyEditorAlias.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateControlIdToPropertyEditorAlias.cs index 74bc2591af..976e2d05b1 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateControlIdToPropertyEditorAlias.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateControlIdToPropertyEditorAlias.cs @@ -1,61 +1,61 @@ -using System; -using Umbraco.Core.Configuration; -using Umbraco.Core.PropertyEditors; - -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven -{ - /// - /// Updates the data in the changed propertyEditorAlias column after it has been changed by ChangeControlIdColumn - /// - [Migration("7.0.0", 1, GlobalSettings.UmbracoMigrationName)] - public class UpdateControlIdToPropertyEditorAlias : MigrationBase - { - public override void Up() - { - //now that the controlId column is renamed and now a string we need to convert - if (Context == null || Context.Database == null) return; - - //we need to get the data and create the migration scripts before we change the actual schema bits below! - var list = Context.Database.Fetch("SELECT pk, controlId FROM cmsDataType"); - foreach (var item in list) - { - Guid legacyId = item.controlId; - var alias = LegacyPropertyEditorIdToAliasConverter.GetAliasFromLegacyId(legacyId); - if (alias != null) - { - //check that the new property editor exists with that alias - var editor = PropertyEditorResolver.Current.GetByAlias(alias); - if (editor == null) - { - //We cannot find a map for this property editor so we're going to make it a label. This is because: - // * we want the upgrade to continue - // * we don't want any data loss - // * developers can change the property editor for the data type at a later time when there's a compatible one - // * editors cannot edit the value with an invalid editor - - Update.Table("cmsDataType").Set(new { propertyEditorAlias = Constants.PropertyEditors.NoEditAlias }).Where(new { item.pk }); - } - else - { - Update.Table("cmsDataType").Set(new { propertyEditorAlias = alias }).Where(new { item.pk }); - } - } - else - { - //We cannot find a map for this property editor so we're going to make it a label. This is because: - // * we want the upgrade to continue - // * we don't want any data loss - // * developers can change the property editor for the data type at a later time when there's a compatible one - // * editors cannot edit the value with an invalid editor - - Update.Table("cmsDataType").Set(new { propertyEditorAlias = Constants.PropertyEditors.NoEditAlias }).Where(new { item.pk }); - } - } - } - - public override void Down() - { - throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); - } - } +using System; +using Umbraco.Core.Configuration; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven +{ + /// + /// Updates the data in the changed propertyEditorAlias column after it has been changed by ChangeControlIdColumn + /// + [Migration("7.0.0", 1, GlobalSettings.UmbracoMigrationName)] + public class UpdateControlIdToPropertyEditorAlias : MigrationBase + { + public override void Up() + { + //now that the controlId column is renamed and now a string we need to convert + if (Context == null || Context.Database == null) return; + + //we need to get the data and create the migration scripts before we change the actual schema bits below! + var list = Context.Database.Fetch("SELECT pk, controlId FROM cmsDataType"); + foreach (var item in list) + { + Guid legacyId = item.controlId; + var alias = LegacyPropertyEditorIdToAliasConverter.GetAliasFromLegacyId(legacyId); + if (alias != null) + { + //check that the new property editor exists with that alias + var editor = PropertyEditorResolver.Current.GetByAlias(alias); + if (editor == null) + { + //We cannot find a map for this property editor so we're going to make it a label. This is because: + // * we want the upgrade to continue + // * we don't want any data loss + // * developers can change the property editor for the data type at a later time when there's a compatible one + // * editors cannot edit the value with an invalid editor + + Update.Table("cmsDataType").Set(new { propertyEditorAlias = Constants.PropertyEditors.NoEditAlias }).Where(new { item.pk }); + } + else + { + Update.Table("cmsDataType").Set(new { propertyEditorAlias = alias }).Where(new { item.pk }); + } + } + else + { + //We cannot find a map for this property editor so we're going to make it a label. This is because: + // * we want the upgrade to continue + // * we don't want any data loss + // * developers can change the property editor for the data type at a later time when there's a compatible one + // * editors cannot edit the value with an invalid editor + + Update.Table("cmsDataType").Set(new { propertyEditorAlias = Constants.PropertyEditors.NoEditAlias }).Where(new { item.pk }); + } + } + } + + public override void Down() + { + throw new NotSupportedException("Cannot downgrade from a version 7 database to a prior version"); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs index a5ae8658bc..e4d2496903 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs @@ -1,37 +1,37 @@ -using System; -using Umbraco.Core.Configuration; - -namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero -{ - [Migration("6.2.0", 0, GlobalSettings.UmbracoMigrationName)] - public class AdditionalIndexesAndKeys : MigrationBase - { - public override void Up() - { - Create.Index("IX_umbracoNodeTrashed").OnTable("umbracoNode").OnColumn("trashed").Ascending().WithOptions().NonClustered(); - Create.Index("IX_cmsContentVersion_ContentId").OnTable("cmsContentVersion").OnColumn("ContentId").Ascending().WithOptions().NonClustered(); - Create.Index("IX_cmsDocument_published").OnTable("cmsDocument").OnColumn("published").Ascending().WithOptions().NonClustered(); - Create.Index("IX_cmsDocument_newest").OnTable("cmsDocument").OnColumn("newest").Ascending().WithOptions().NonClustered(); - } - - public override void Down() - { - throw new NotImplementedException(); - } - } - - [Migration("6.2.0", 2, GlobalSettings.UmbracoMigrationName)] - public class ChangePasswordColumn : MigrationBase - { - public override void Up() - { - //up to 500 chars - Alter.Table("umbracoUser").AlterColumn("userPassword").AsString(500).NotNullable(); - } - - public override void Down() - { - throw new NotImplementedException(); - } - } +using System; +using Umbraco.Core.Configuration; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero +{ + [Migration("6.2.0", 0, GlobalSettings.UmbracoMigrationName)] + public class AdditionalIndexesAndKeys : MigrationBase + { + public override void Up() + { + Create.Index("IX_umbracoNodeTrashed").OnTable("umbracoNode").OnColumn("trashed").Ascending().WithOptions().NonClustered(); + Create.Index("IX_cmsContentVersion_ContentId").OnTable("cmsContentVersion").OnColumn("ContentId").Ascending().WithOptions().NonClustered(); + Create.Index("IX_cmsDocument_published").OnTable("cmsDocument").OnColumn("published").Ascending().WithOptions().NonClustered(); + Create.Index("IX_cmsDocument_newest").OnTable("cmsDocument").OnColumn("newest").Ascending().WithOptions().NonClustered(); + } + + public override void Down() + { + throw new NotImplementedException(); + } + } + + [Migration("6.2.0", 2, GlobalSettings.UmbracoMigrationName)] + public class ChangePasswordColumn : MigrationBase + { + public override void Up() + { + //up to 500 chars + Alter.Table("umbracoUser").AlterColumn("userPassword").AsString(500).NotNullable(); + } + + public override void Down() + { + throw new NotImplementedException(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Relators/MacroPropertyRelator.cs b/src/Umbraco.Core/Persistence/Relators/MacroPropertyRelator.cs index 39190a69f9..fb563fa25c 100644 --- a/src/Umbraco.Core/Persistence/Relators/MacroPropertyRelator.cs +++ b/src/Umbraco.Core/Persistence/Relators/MacroPropertyRelator.cs @@ -1,47 +1,47 @@ -using System.Collections.Generic; -using Umbraco.Core.Models.Rdbms; - -namespace Umbraco.Core.Persistence.Relators -{ - internal class MacroPropertyRelator - { - internal MacroDto Current; - - internal MacroDto Map(MacroDto a, MacroPropertyDto p) - { - // Terminating call. Since we can return null from this function - // we need to be ready for PetaPoco to callback later with null - // parameters - if (a == null) - return Current; - - // Is this the same DictionaryItem as the current one we're processing - if (Current != null && Current.Id == a.Id) - { - // Yes, just add this MacroPropertyDtos to the current item's collection - Current.MacroPropertyDtos.Add(p); - - // Return null to indicate we're not done with this Macro yet - return null; - } - - // This is a different Macro to the current one, or this is the - // first time through and we don't have one yet - - // Save the current Macro - var prev = Current; - - // Setup the new current Macro - Current = a; - Current.MacroPropertyDtos = new List(); - //this can be null since we are doing a left join - if (p.Alias != null) - { - Current.MacroPropertyDtos.Add(p); - } - - // Return the now populated previous Macro (or null if first time through) - return prev; - } - } +using System.Collections.Generic; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Relators +{ + internal class MacroPropertyRelator + { + internal MacroDto Current; + + internal MacroDto Map(MacroDto a, MacroPropertyDto p) + { + // Terminating call. Since we can return null from this function + // we need to be ready for PetaPoco to callback later with null + // parameters + if (a == null) + return Current; + + // Is this the same DictionaryItem as the current one we're processing + if (Current != null && Current.Id == a.Id) + { + // Yes, just add this MacroPropertyDtos to the current item's collection + Current.MacroPropertyDtos.Add(p); + + // Return null to indicate we're not done with this Macro yet + return null; + } + + // This is a different Macro to the current one, or this is the + // first time through and we don't have one yet + + // Save the current Macro + var prev = Current; + + // Setup the new current Macro + Current = a; + Current.MacroPropertyDtos = new List(); + //this can be null since we are doing a left join + if (p.Alias != null) + { + Current.MacroPropertyDtos.Add(p); + } + + // Return the now populated previous Macro (or null if first time through) + return prev; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Relators/PropertyTypePropertyGroupRelator.cs b/src/Umbraco.Core/Persistence/Relators/PropertyTypePropertyGroupRelator.cs index f02ca8e1b1..29342e283f 100644 --- a/src/Umbraco.Core/Persistence/Relators/PropertyTypePropertyGroupRelator.cs +++ b/src/Umbraco.Core/Persistence/Relators/PropertyTypePropertyGroupRelator.cs @@ -1,55 +1,55 @@ -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core.Models.Rdbms; - -namespace Umbraco.Core.Persistence.Relators -{ - internal class PropertyTypePropertyGroupRelator - { - internal MemberTypeReadOnlyDto Current; - - internal MemberTypeReadOnlyDto Map(MemberTypeReadOnlyDto a, PropertyTypeReadOnlyDto p, PropertyTypeGroupReadOnlyDto g) - { - // Terminating call. Since we can return null from this function - // we need to be ready for PetaPoco to callback later with null - // parameters - if (a == null) - return Current; - - // Is this the same MemberTypeReadOnlyDto as the current one we're processing - if (Current != null && Current.UniqueId == a.UniqueId) - { - // Yes, just add this PropertyTypeReadOnlyDto to the current MemberTypeReadOnlyDto's collection - Current.PropertyTypes.Add(p); - - if (g.Id.HasValue && Current.PropertyTypeGroups != null && Current.PropertyTypeGroups.Any(x => x.Id == g.Id.Value) == false) - Current.PropertyTypeGroups.Add(g); - - // Return null to indicate we're not done with this MemberTypeReadOnlyDto yet - return null; - } - - // This is a different MemberTypeReadOnlyDto to the current one, or this is the - // first time through and we don't have a Tab yet - - // Save the current MemberTypeReadOnlyDto - var prev = Current; - - // Setup the new current MemberTypeReadOnlyDto - Current = a; - Current.PropertyTypes = new List(); - //this can be null since we are doing a left join - if (p.Id.HasValue) - { - Current.PropertyTypes.Add(p); - } - - Current.PropertyTypeGroups = new List(); - if (g.Id.HasValue) - Current.PropertyTypeGroups.Add(g); - - // Return the now populated previous MemberTypeReadOnlyDto (or null if first time through) - return prev; - } - } +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Relators +{ + internal class PropertyTypePropertyGroupRelator + { + internal MemberTypeReadOnlyDto Current; + + internal MemberTypeReadOnlyDto Map(MemberTypeReadOnlyDto a, PropertyTypeReadOnlyDto p, PropertyTypeGroupReadOnlyDto g) + { + // Terminating call. Since we can return null from this function + // we need to be ready for PetaPoco to callback later with null + // parameters + if (a == null) + return Current; + + // Is this the same MemberTypeReadOnlyDto as the current one we're processing + if (Current != null && Current.UniqueId == a.UniqueId) + { + // Yes, just add this PropertyTypeReadOnlyDto to the current MemberTypeReadOnlyDto's collection + Current.PropertyTypes.Add(p); + + if (g.Id.HasValue && Current.PropertyTypeGroups != null && Current.PropertyTypeGroups.Any(x => x.Id == g.Id.Value) == false) + Current.PropertyTypeGroups.Add(g); + + // Return null to indicate we're not done with this MemberTypeReadOnlyDto yet + return null; + } + + // This is a different MemberTypeReadOnlyDto to the current one, or this is the + // first time through and we don't have a Tab yet + + // Save the current MemberTypeReadOnlyDto + var prev = Current; + + // Setup the new current MemberTypeReadOnlyDto + Current = a; + Current.PropertyTypes = new List(); + //this can be null since we are doing a left join + if (p.Id.HasValue) + { + Current.PropertyTypes.Add(p); + } + + Current.PropertyTypeGroups = new List(); + if (g.Id.HasValue) + Current.PropertyTypeGroups.Add(g); + + // Return the now populated previous MemberTypeReadOnlyDto (or null if first time through) + return prev; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMacroRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMacroRepository.cs index b800728e67..496012ef10 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMacroRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMacroRepository.cs @@ -1,12 +1,12 @@ -using System.Collections.Generic; -using Umbraco.Core.Models; - -namespace Umbraco.Core.Persistence.Repositories -{ - internal interface IMacroRepository : IRepositoryQueryable - { - - //IEnumerable GetAll(params string[] aliases); - - } +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Persistence.Repositories +{ + internal interface IMacroRepository : IRepositoryQueryable + { + + //IEnumerable GetAll(params string[] aliases); + + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberTypeRepository.cs index 41194c29e3..9e91b1c87c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberTypeRepository.cs @@ -1,9 +1,9 @@ -using Umbraco.Core.Models; - -namespace Umbraco.Core.Persistence.Repositories -{ - public interface IMemberTypeRepository : IRepositoryQueryable - { - - } +using Umbraco.Core.Models; + +namespace Umbraco.Core.Persistence.Repositories +{ + public interface IMemberTypeRepository : IRepositoryQueryable + { + + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITagsRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITagsRepository.cs index 9dec8eef2f..cca0145619 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITagsRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITagsRepository.cs @@ -1,57 +1,57 @@ -using System.Collections.Generic; -using Umbraco.Core.Models; - -namespace Umbraco.Core.Persistence.Repositories -{ - public interface ITagsRepository : IRepositoryQueryable - { - /// - /// Returns all tags for an entity type (content/media/member) - /// - /// - /// Optional group - /// - IEnumerable GetTagsForEntityType(TaggableObjectTypes objectType, string group = null); - - /// - /// Returns all tags that exist on the content item - Content/Media/Member - /// - /// The content item id to get tags for - /// Optional group - /// - IEnumerable GetTagsForEntity(int contentId, string group = null); - - /// - /// Returns all tags that exist on the content item for the property specified - Content/Media/Member - /// - /// The content item id to get tags for - /// The property alias to get tags for - /// Optional group - /// - IEnumerable GetTagsForProperty(int contentId, string propertyTypeAlias, string group = null); - - /// - /// Assigns the given tags to a content item's property - /// - /// - /// - /// The tags to assign - /// - /// If set to true, this will replace all tags with the given tags, - /// if false this will append the tags that already exist for the content item - /// - /// - /// - /// This can also be used to remove all tags from a property by specifying replaceTags = true and an empty tag list. - /// - void AssignTagsToProperty(int contentId, string propertyTypeAlias, IEnumerable tags, bool replaceTags); - - /// - /// Removes any of the given tags from the property association - /// - /// - /// - /// The tags to remove from the property - void RemoveTagsFromProperty(int contentId, string propertyTypeAlias, IEnumerable tags); - } +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Persistence.Repositories +{ + public interface ITagsRepository : IRepositoryQueryable + { + /// + /// Returns all tags for an entity type (content/media/member) + /// + /// + /// Optional group + /// + IEnumerable GetTagsForEntityType(TaggableObjectTypes objectType, string group = null); + + /// + /// Returns all tags that exist on the content item - Content/Media/Member + /// + /// The content item id to get tags for + /// Optional group + /// + IEnumerable GetTagsForEntity(int contentId, string group = null); + + /// + /// Returns all tags that exist on the content item for the property specified - Content/Media/Member + /// + /// The content item id to get tags for + /// The property alias to get tags for + /// Optional group + /// + IEnumerable GetTagsForProperty(int contentId, string propertyTypeAlias, string group = null); + + /// + /// Assigns the given tags to a content item's property + /// + /// + /// + /// The tags to assign + /// + /// If set to true, this will replace all tags with the given tags, + /// if false this will append the tags that already exist for the content item + /// + /// + /// + /// This can also be used to remove all tags from a property by specifying replaceTags = true and an empty tag list. + /// + void AssignTagsToProperty(int contentId, string propertyTypeAlias, IEnumerable tags, bool replaceTags); + + /// + /// Removes any of the given tags from the property association + /// + /// + /// + /// The tags to remove from the property + void RemoveTagsFromProperty(int contentId, string propertyTypeAlias, IEnumerable tags); + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs index 01ecc981fd..805177be9a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs @@ -1,218 +1,218 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core.Models; -using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.Models.Rdbms; -using Umbraco.Core.Persistence.Caching; -using Umbraco.Core.Persistence.Factories; -using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.Relators; -using Umbraco.Core.Persistence.SqlSyntax; -using Umbraco.Core.Persistence.UnitOfWork; - -namespace Umbraco.Core.Persistence.Repositories -{ - internal class MacroRepository : PetaPocoRepositoryBase, IMacroRepository - { - public MacroRepository(IDatabaseUnitOfWork work) : base(work) - { - } - - public MacroRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache) : base(work, cache) - { - } - - protected override IMacro PerformGet(int id) - { - var sql = GetBaseQuery(false); - sql.Where(GetBaseWhereClause(), new { Id = id }); - - var macroDto = Database.Fetch(new MacroPropertyRelator().Map, sql).FirstOrDefault(); - if (macroDto == null) - return null; - - var factory = new MacroFactory(); - var entity = factory.BuildEntity(macroDto); - - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - ((TracksChangesEntityBase)entity).ResetDirtyProperties(false); - - return entity; - } - - protected override IEnumerable PerformGetAll(params int[] ids) - { - if (ids.Any()) - { - return PerformGetAllOnIds(ids); - } - - var sql = GetBaseQuery(false); - - return ConvertFromDtos(Database.Fetch(new MacroPropertyRelator().Map, sql)) - .ToArray();// we don't want to re-iterate again! - } - - private IEnumerable PerformGetAllOnIds(params int[] ids) - { - if (ids.Any() == false) yield break; - foreach (var id in ids) - { - yield return Get(id); - } - } - - private IEnumerable ConvertFromDtos(IEnumerable dtos) - { - var factory = new MacroFactory(); - foreach (var entity in dtos.Select(factory.BuildEntity)) - { - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - ((TracksChangesEntityBase)entity).ResetDirtyProperties(false); - - yield return entity; - } - } - - protected override IEnumerable PerformGetByQuery(IQuery query) - { - var sqlClause = GetBaseQuery(false); - var translator = new SqlTranslator(sqlClause, query); - var sql = translator.Translate(); - - var dtos = Database.Fetch(new MacroPropertyRelator().Map, sql); - - foreach (var dto in dtos) - { - yield return Get(dto.Id); - } - } - - protected override Sql GetBaseQuery(bool isCount) - { - var sql = new Sql(); - if (isCount) - { - sql.Select("COUNT(*)").From(); - } - else - { - return GetBaseQuery(); - } - return sql; - } - - private static Sql GetBaseQuery() - { - var sql = new Sql(); - sql.Select("*") - .From() - .LeftJoin() - .On(left => left.Id, right => right.Macro); - return sql; - } - - protected override string GetBaseWhereClause() - { - return "cmsMacro.id = @Id"; - } - - protected override IEnumerable GetDeleteClauses() - { - var list = new List - { - "DELETE FROM cmsMacroProperty WHERE macro = @Id", - "DELETE FROM cmsMacro WHERE id = @Id" - }; - return list; - } - - protected override Guid NodeObjectTypeId - { - get { throw new NotImplementedException(); } - } - - protected override void PersistNewItem(IMacro entity) - { - ((Entity)entity).AddingEntity(); - - var factory = new MacroFactory(); - var dto = factory.BuildDto(entity); - - var id = Convert.ToInt32(Database.Insert(dto)); - entity.Id = id; - - foreach (var propDto in dto.MacroPropertyDtos) - { - //need to set the id explicitly here - propDto.Macro = id; - var propId = Convert.ToInt32(Database.Insert(propDto)); - entity.Properties[propDto.Alias].Id = propId; - } - - ((ICanBeDirty)entity).ResetDirtyProperties(); - } - - protected override void PersistUpdatedItem(IMacro entity) - { - ((Entity)entity).UpdatingEntity(); - - var factory = new MacroFactory(); - var dto = factory.BuildDto(entity); - - Database.Update(dto); - - //update the sections if they've changed - var macro = (Macro)entity; - if (macro.IsPropertyDirty("Properties")) - { - //for any that exist on the object, we need to determine if we need to update or insert - foreach (var propDto in dto.MacroPropertyDtos) - { - if (macro.AddedProperties.Contains(propDto.Alias)) - { - //we need to insert since this was added and re-assign the new id - var propId = Convert.ToInt32(Database.Insert(propDto)); - macro.Properties[propDto.Alias].Id = propId; - } - else - { - Database.Update(propDto); - } - } - - //now we need to delete any props that have been removed - foreach (var propAlias in macro.RemovedProperties) - { - //delete the property - Database.Delete("WHERE macro=@macroId AND macroPropertyAlias=@propAlias", - new { macroId = macro.Id, propAlias = propAlias }); - } - } - - ((ICanBeDirty)entity).ResetDirtyProperties(); - } - - //public IEnumerable GetAll(params string[] aliases) - //{ - // if (aliases.Any()) - // { - // var q = new Query(); - // foreach (var alias in aliases) - // { - // q.Where(macro => macro.Alias == alias); - // } - - // var wheres = string.Join(" OR ", q.WhereClauses()); - // } - // else - // { - // return GetAll(new int[] {}); - // } - - //} - } +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.Caching; +using Umbraco.Core.Persistence.Factories; +using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.Relators; +using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Core.Persistence.UnitOfWork; + +namespace Umbraco.Core.Persistence.Repositories +{ + internal class MacroRepository : PetaPocoRepositoryBase, IMacroRepository + { + public MacroRepository(IDatabaseUnitOfWork work) : base(work) + { + } + + public MacroRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache) : base(work, cache) + { + } + + protected override IMacro PerformGet(int id) + { + var sql = GetBaseQuery(false); + sql.Where(GetBaseWhereClause(), new { Id = id }); + + var macroDto = Database.Fetch(new MacroPropertyRelator().Map, sql).FirstOrDefault(); + if (macroDto == null) + return null; + + var factory = new MacroFactory(); + var entity = factory.BuildEntity(macroDto); + + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + ((TracksChangesEntityBase)entity).ResetDirtyProperties(false); + + return entity; + } + + protected override IEnumerable PerformGetAll(params int[] ids) + { + if (ids.Any()) + { + return PerformGetAllOnIds(ids); + } + + var sql = GetBaseQuery(false); + + return ConvertFromDtos(Database.Fetch(new MacroPropertyRelator().Map, sql)) + .ToArray();// we don't want to re-iterate again! + } + + private IEnumerable PerformGetAllOnIds(params int[] ids) + { + if (ids.Any() == false) yield break; + foreach (var id in ids) + { + yield return Get(id); + } + } + + private IEnumerable ConvertFromDtos(IEnumerable dtos) + { + var factory = new MacroFactory(); + foreach (var entity in dtos.Select(factory.BuildEntity)) + { + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + ((TracksChangesEntityBase)entity).ResetDirtyProperties(false); + + yield return entity; + } + } + + protected override IEnumerable PerformGetByQuery(IQuery query) + { + var sqlClause = GetBaseQuery(false); + var translator = new SqlTranslator(sqlClause, query); + var sql = translator.Translate(); + + var dtos = Database.Fetch(new MacroPropertyRelator().Map, sql); + + foreach (var dto in dtos) + { + yield return Get(dto.Id); + } + } + + protected override Sql GetBaseQuery(bool isCount) + { + var sql = new Sql(); + if (isCount) + { + sql.Select("COUNT(*)").From(); + } + else + { + return GetBaseQuery(); + } + return sql; + } + + private static Sql GetBaseQuery() + { + var sql = new Sql(); + sql.Select("*") + .From() + .LeftJoin() + .On(left => left.Id, right => right.Macro); + return sql; + } + + protected override string GetBaseWhereClause() + { + return "cmsMacro.id = @Id"; + } + + protected override IEnumerable GetDeleteClauses() + { + var list = new List + { + "DELETE FROM cmsMacroProperty WHERE macro = @Id", + "DELETE FROM cmsMacro WHERE id = @Id" + }; + return list; + } + + protected override Guid NodeObjectTypeId + { + get { throw new NotImplementedException(); } + } + + protected override void PersistNewItem(IMacro entity) + { + ((Entity)entity).AddingEntity(); + + var factory = new MacroFactory(); + var dto = factory.BuildDto(entity); + + var id = Convert.ToInt32(Database.Insert(dto)); + entity.Id = id; + + foreach (var propDto in dto.MacroPropertyDtos) + { + //need to set the id explicitly here + propDto.Macro = id; + var propId = Convert.ToInt32(Database.Insert(propDto)); + entity.Properties[propDto.Alias].Id = propId; + } + + ((ICanBeDirty)entity).ResetDirtyProperties(); + } + + protected override void PersistUpdatedItem(IMacro entity) + { + ((Entity)entity).UpdatingEntity(); + + var factory = new MacroFactory(); + var dto = factory.BuildDto(entity); + + Database.Update(dto); + + //update the sections if they've changed + var macro = (Macro)entity; + if (macro.IsPropertyDirty("Properties")) + { + //for any that exist on the object, we need to determine if we need to update or insert + foreach (var propDto in dto.MacroPropertyDtos) + { + if (macro.AddedProperties.Contains(propDto.Alias)) + { + //we need to insert since this was added and re-assign the new id + var propId = Convert.ToInt32(Database.Insert(propDto)); + macro.Properties[propDto.Alias].Id = propId; + } + else + { + Database.Update(propDto); + } + } + + //now we need to delete any props that have been removed + foreach (var propAlias in macro.RemovedProperties) + { + //delete the property + Database.Delete("WHERE macro=@macroId AND macroPropertyAlias=@propAlias", + new { macroId = macro.Id, propAlias = propAlias }); + } + } + + ((ICanBeDirty)entity).ResetDirtyProperties(); + } + + //public IEnumerable GetAll(params string[] aliases) + //{ + // if (aliases.Any()) + // { + // var q = new Query(); + // foreach (var alias in aliases) + // { + // q.Where(macro => macro.Alias == alias); + // } + + // var wheres = string.Join(" OR ", q.WhereClauses()); + // } + // else + // { + // return GetAll(new int[] {}); + // } + + //} + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/TagsRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TagsRepository.cs index b029d31406..180b532b02 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TagsRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TagsRepository.cs @@ -1,431 +1,431 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Umbraco.Core.Models; -using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.Models.Rdbms; -using Umbraco.Core.Persistence.Caching; -using Umbraco.Core.Persistence.Factories; -using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; -using Umbraco.Core.Persistence.UnitOfWork; - -namespace Umbraco.Core.Persistence.Repositories -{ - internal class TagsRepository : PetaPocoRepositoryBase, ITagsRepository - { - protected TagsRepository(IDatabaseUnitOfWork work) - : this(work, RuntimeCacheProvider.Current) - { - } - - internal TagsRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache) - : base(work, cache) - { - } - - protected override ITag PerformGet(int id) - { - var sql = GetBaseQuery(false); - sql.Where(GetBaseWhereClause(), new { Id = id }); - - var tagDto = Database.Fetch(sql).FirstOrDefault(); - if (tagDto == null) - return null; - - var factory = new TagFactory(); - var entity = factory.BuildEntity(tagDto); - - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - ((TracksChangesEntityBase)entity).ResetDirtyProperties(false); - - return entity; - } - - protected override IEnumerable PerformGetAll(params int[] ids) - { - if (ids.Any()) - { - return PerformGetAllOnIds(ids); - } - - var sql = GetBaseQuery(false); - - return ConvertFromDtos(Database.Fetch(sql)) - .ToArray();// we don't want to re-iterate again! - } - - private IEnumerable PerformGetAllOnIds(params int[] ids) - { - if (ids.Any() == false) yield break; - foreach (var id in ids) - { - yield return Get(id); - } - } - - private IEnumerable ConvertFromDtos(IEnumerable dtos) - { - var factory = new TagFactory(); - foreach (var entity in dtos.Select(factory.BuildEntity)) - { - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - ((TracksChangesEntityBase)entity).ResetDirtyProperties(false); - yield return entity; - } - } - - protected override IEnumerable PerformGetByQuery(IQuery query) - { - var sqlClause = GetBaseQuery(false); - var translator = new SqlTranslator(sqlClause, query); - var sql = translator.Translate(); - - var dtos = Database.Fetch(sql); - - foreach (var dto in dtos) - { - yield return Get(dto.Id); - } - } - - protected override Sql GetBaseQuery(bool isCount) - { - var sql = new Sql(); - if (isCount) - { - sql.Select("COUNT(*)").From(); - } - else - { - return GetBaseQuery(); - } - return sql; - } - - private static Sql GetBaseQuery() - { - var sql = new Sql(); - sql.Select("*").From(); - return sql; - } - - protected override string GetBaseWhereClause() - { - return "id = @Id"; - } - - protected override IEnumerable GetDeleteClauses() - { - var list = new List - { - "DELETE FROM cmsTagRelationship WHERE tagId = @Id", - "DELETE FROM cmsTags WHERE id = @Id" - }; - return list; - } - - protected override Guid NodeObjectTypeId - { - get { throw new NotImplementedException(); } - } - - protected override void PersistNewItem(ITag entity) - { - ((Entity)entity).AddingEntity(); - - var factory = new TagFactory(); - var dto = factory.BuildDto(entity); - - var id = Convert.ToInt32(Database.Insert(dto)); - entity.Id = id; - - ((ICanBeDirty)entity).ResetDirtyProperties(); - } - - protected override void PersistUpdatedItem(ITag entity) - { - ((Entity)entity).UpdatingEntity(); - - var factory = new TagFactory(); - var dto = factory.BuildDto(entity); - - Database.Update(dto); - - ((ICanBeDirty)entity).ResetDirtyProperties(); - } - - //TODO: Consider caching implications. - - public IEnumerable GetTagsForEntityType(TaggableObjectTypes objectType, string group = null) - { - var nodeObjectType = GetNodeObjectType(objectType); - - var sql = new Sql() - .Select("DISTINCT cmsTags.*") - .From() - .InnerJoin() - .On(left => left.TagId, right => right.Id) - .InnerJoin() - .On(left => left.NodeId, right => right.NodeId) - .InnerJoin() - .On(left => left.NodeId, right => right.NodeId) - .Where(dto => dto.NodeObjectType == nodeObjectType); - - if (group.IsNullOrWhiteSpace() == false) - { - sql = sql.Where(dto => dto.Group == group); - } - - var factory = new TagFactory(); - - return Database.Fetch(sql).Select(factory.BuildEntity); - } - - public IEnumerable GetTagsForEntity(int contentId, string group = null) - { - var sql = new Sql() - .Select("DISTINCT cmsTags.*") - .From() - .InnerJoin() - .On(left => left.TagId, right => right.Id) - .Where(dto => dto.NodeId == contentId); - - if (group.IsNullOrWhiteSpace() == false) - { - sql = sql.Where(dto => dto.Group == group); - } - - var factory = new TagFactory(); - - return Database.Fetch(sql).Select(factory.BuildEntity); - } - - public IEnumerable GetTagsForProperty(int contentId, string propertyTypeAlias, string group = null) - { - var sql = new Sql() - .Select("DISTINCT cmsTags.*") - .From() - .InnerJoin() - .On(left => left.TagId, right => right.Id) - .InnerJoin() - .On(left => left.Id, right => right.PropertyTypeId) - .Where(dto => dto.NodeId == contentId) - .Where(dto => dto.Alias == propertyTypeAlias); - - if (group.IsNullOrWhiteSpace() == false) - { - sql = sql.Where(dto => dto.Group == group); - } - - var factory = new TagFactory(); - - return Database.Fetch(sql).Select(factory.BuildEntity); - } - - /// - /// Assigns the given tags to a content item's property - /// - /// - /// - /// The tags to assign - /// - /// If set to true, this will replace all tags with the given tags, - /// if false this will append the tags that already exist for the content item - /// - /// - /// - /// This can also be used to remove all tags from a property by specifying replaceTags = true and an empty tag list. - /// - public void AssignTagsToProperty(int contentId, string propertyTypeAlias, IEnumerable tags, bool replaceTags) - { - //First we need to ensure there are no duplicates - var asArray = tags.Distinct(new TagComparer()).ToArray(); - - //we don't have to do anything if there are no tags assigning and we're not replacing them - if (asArray.Length == 0 && replaceTags == false) - { - return; - } - - var propertyTypeId = EnsureContentProperty(contentId, propertyTypeAlias); - - //next check if we're removing all of the tags - if (asArray.Length == 0 && replaceTags) - { - Database.Execute("DELETE FROM cmsTagRelationship WHERE nodeId=" + contentId + " AND propertyTypeId=" + propertyTypeId); - return; - } - - //ok so now we're actually assigning tags... - //NOTE: There's some very clever logic in the umbraco.cms.businesslogic.Tags.Tag to insert tags where they don't exist, - // and assign where they don't exist which we've borrowed here. The queries are pretty zany but work, otherwise we'll end up - // with quite a few additional queries. - - //do all this in one transaction - using (var trans = Database.GetTransaction()) - { - //var factory = new TagFactory(); - - var tagSetSql = GetTagSet(asArray); - - //adds any tags found in the collection that aren't in cmsTag - var insertTagsSql = string.Concat("insert into cmsTags (Tag,", - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Group"), - ") ", - " select TagSet.Tag, TagSet.", - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Group"), - " from ", - tagSetSql, - " left outer join cmsTags on (TagSet.Tag = cmsTags.Tag and TagSet.", - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Group"), - " = cmsTags.", - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Group"), - ")", - " where cmsTags.Id is null "); - //insert the tags that don't exist - Database.Execute(insertTagsSql); - - if (replaceTags) - { - //if we are replacing the tags then remove them first - Database.Execute("DELETE FROM cmsTagRelationship WHERE nodeId=" + contentId + " AND propertyTypeId=" + propertyTypeId); - } - - //adds any tags found in csv that aren't in tagrelationships - var insertTagRelationsSql = string.Concat("insert into cmsTagRelationship (tagId,nodeId,propertyTypeId) ", - "select NewTagsSet.Id, " + contentId + ", " + propertyTypeId + " from ", - "( ", - "select NewTags.Id from ", - tagSetSql, - " inner join cmsTags as NewTags on (TagSet.Tag = NewTags.Tag and TagSet.", - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Group"), - " = TagSet.", - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Group"), - ") ", - ") as NewTagsSet ", - "left outer join cmsTagRelationship ", - "on (cmsTagRelationship.TagId = NewTagsSet.Id and cmsTagRelationship.nodeId = ", - contentId, - "and cmsTagRelationship.propertyTypeId = ", - propertyTypeId, - ") ", - "where cmsTagRelationship.tagId is null "); - - //insert the tags relations that don't exist - Database.Execute(insertTagRelationsSql); - - //GO! - trans.Complete(); - } - } - - /// - /// Removes any of the given tags from the property association - /// - /// - /// - /// The tags to remove from the property - public void RemoveTagsFromProperty(int contentId, string propertyTypeAlias, IEnumerable tags) - { - var propertyTypeId = EnsureContentProperty(contentId, propertyTypeAlias); - - var tagSetSql = GetTagSet(tags); - - var deleteSql = string.Concat("DELETE FROM cmsTagRelationship WHERE nodeId = ", - contentId, - " AND propertyTypeId = ", - propertyTypeId, - " AND tagId IN ", - "(SELECT id FROM cmsTags INNER JOIN ", - tagSetSql, - " ON (TagSet.Tag = cmsTags.Tag and TagSet.[Group] = cmsTags.[Group]))"); - - Database.Execute(deleteSql); - } - - private Guid GetNodeObjectType(TaggableObjectTypes type) - { - switch (type) - { - case TaggableObjectTypes.Content: - return new Guid(Constants.ObjectTypes.Document); - case TaggableObjectTypes.Media: - return new Guid(Constants.ObjectTypes.Media); - case TaggableObjectTypes.Member: - return new Guid(Constants.ObjectTypes.Member); - default: - throw new ArgumentOutOfRangeException("type"); - } - } - - /// - /// This is a clever way to produce an SQL statement like this: - /// - /// (select 'Spacesdd' as Tag, 'default' as [Group] - /// union - /// select 'Cool' as Tag, 'default' as [Group] - /// ) as TagSet - /// - /// This allows us to use the tags to be inserted as a temporary in memory table. - /// - /// - /// - private static string GetTagSet(IEnumerable tagsToInsert) - { - var array = tagsToInsert.Select(tag => string.Format("select '{0}' as Tag, '{1}' as [Group]", tag.Text, tag.Group)).ToArray(); - return "(" + string.Join(" union ", array).Replace(" ", " ") + ") as TagSet"; - } - - private class TagComparer : IEqualityComparer - { - public bool Equals(ITag x, ITag y) - { - return x.Text == y.Text && x.Group == y.Group; - } - - public int GetHashCode(ITag obj) - { - unchecked - { - return (obj.Text.GetHashCode() * 397) ^ obj.Group.GetHashCode(); - } - } - } - - /// - /// Ensures the content and property alias exist, then returns the property type id for the alias - /// - /// - /// - /// - private int EnsureContentProperty(int contentId, string propertyAlias) - { - //ensure that there's content and a property to assign - NOTE: we cannot use the content repository here - // because the content repository requires one of us TagsRepository instance, then we'll have circular dependencies - // instead we'll just look it up ourselves. - - var sql = new Sql() - .Select("cmsPropertyType.id") - .From() - .InnerJoin() - .On(left => left.NodeId, right => right.ContentTypeId) - .InnerJoin() - .On(left => left.ContentTypeId, right => right.NodeId) - .Where(dto => dto.NodeId == contentId) - .Where(dto => dto.Alias == propertyAlias); - - var result = Database.Fetch(sql).ToArray(); - - if (result.Length == 0) - { - throw new InvalidOperationException("Cannot modify tags for content id " + contentId + " and property " + propertyAlias + " because the content item does not exist with this property"); - } - - return result.First(); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.Caching; +using Umbraco.Core.Persistence.Factories; +using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Core.Persistence.UnitOfWork; + +namespace Umbraco.Core.Persistence.Repositories +{ + internal class TagsRepository : PetaPocoRepositoryBase, ITagsRepository + { + protected TagsRepository(IDatabaseUnitOfWork work) + : this(work, RuntimeCacheProvider.Current) + { + } + + internal TagsRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache) + : base(work, cache) + { + } + + protected override ITag PerformGet(int id) + { + var sql = GetBaseQuery(false); + sql.Where(GetBaseWhereClause(), new { Id = id }); + + var tagDto = Database.Fetch(sql).FirstOrDefault(); + if (tagDto == null) + return null; + + var factory = new TagFactory(); + var entity = factory.BuildEntity(tagDto); + + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + ((TracksChangesEntityBase)entity).ResetDirtyProperties(false); + + return entity; + } + + protected override IEnumerable PerformGetAll(params int[] ids) + { + if (ids.Any()) + { + return PerformGetAllOnIds(ids); + } + + var sql = GetBaseQuery(false); + + return ConvertFromDtos(Database.Fetch(sql)) + .ToArray();// we don't want to re-iterate again! + } + + private IEnumerable PerformGetAllOnIds(params int[] ids) + { + if (ids.Any() == false) yield break; + foreach (var id in ids) + { + yield return Get(id); + } + } + + private IEnumerable ConvertFromDtos(IEnumerable dtos) + { + var factory = new TagFactory(); + foreach (var entity in dtos.Select(factory.BuildEntity)) + { + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + ((TracksChangesEntityBase)entity).ResetDirtyProperties(false); + yield return entity; + } + } + + protected override IEnumerable PerformGetByQuery(IQuery query) + { + var sqlClause = GetBaseQuery(false); + var translator = new SqlTranslator(sqlClause, query); + var sql = translator.Translate(); + + var dtos = Database.Fetch(sql); + + foreach (var dto in dtos) + { + yield return Get(dto.Id); + } + } + + protected override Sql GetBaseQuery(bool isCount) + { + var sql = new Sql(); + if (isCount) + { + sql.Select("COUNT(*)").From(); + } + else + { + return GetBaseQuery(); + } + return sql; + } + + private static Sql GetBaseQuery() + { + var sql = new Sql(); + sql.Select("*").From(); + return sql; + } + + protected override string GetBaseWhereClause() + { + return "id = @Id"; + } + + protected override IEnumerable GetDeleteClauses() + { + var list = new List + { + "DELETE FROM cmsTagRelationship WHERE tagId = @Id", + "DELETE FROM cmsTags WHERE id = @Id" + }; + return list; + } + + protected override Guid NodeObjectTypeId + { + get { throw new NotImplementedException(); } + } + + protected override void PersistNewItem(ITag entity) + { + ((Entity)entity).AddingEntity(); + + var factory = new TagFactory(); + var dto = factory.BuildDto(entity); + + var id = Convert.ToInt32(Database.Insert(dto)); + entity.Id = id; + + ((ICanBeDirty)entity).ResetDirtyProperties(); + } + + protected override void PersistUpdatedItem(ITag entity) + { + ((Entity)entity).UpdatingEntity(); + + var factory = new TagFactory(); + var dto = factory.BuildDto(entity); + + Database.Update(dto); + + ((ICanBeDirty)entity).ResetDirtyProperties(); + } + + //TODO: Consider caching implications. + + public IEnumerable GetTagsForEntityType(TaggableObjectTypes objectType, string group = null) + { + var nodeObjectType = GetNodeObjectType(objectType); + + var sql = new Sql() + .Select("DISTINCT cmsTags.*") + .From() + .InnerJoin() + .On(left => left.TagId, right => right.Id) + .InnerJoin() + .On(left => left.NodeId, right => right.NodeId) + .InnerJoin() + .On(left => left.NodeId, right => right.NodeId) + .Where(dto => dto.NodeObjectType == nodeObjectType); + + if (group.IsNullOrWhiteSpace() == false) + { + sql = sql.Where(dto => dto.Group == group); + } + + var factory = new TagFactory(); + + return Database.Fetch(sql).Select(factory.BuildEntity); + } + + public IEnumerable GetTagsForEntity(int contentId, string group = null) + { + var sql = new Sql() + .Select("DISTINCT cmsTags.*") + .From() + .InnerJoin() + .On(left => left.TagId, right => right.Id) + .Where(dto => dto.NodeId == contentId); + + if (group.IsNullOrWhiteSpace() == false) + { + sql = sql.Where(dto => dto.Group == group); + } + + var factory = new TagFactory(); + + return Database.Fetch(sql).Select(factory.BuildEntity); + } + + public IEnumerable GetTagsForProperty(int contentId, string propertyTypeAlias, string group = null) + { + var sql = new Sql() + .Select("DISTINCT cmsTags.*") + .From() + .InnerJoin() + .On(left => left.TagId, right => right.Id) + .InnerJoin() + .On(left => left.Id, right => right.PropertyTypeId) + .Where(dto => dto.NodeId == contentId) + .Where(dto => dto.Alias == propertyTypeAlias); + + if (group.IsNullOrWhiteSpace() == false) + { + sql = sql.Where(dto => dto.Group == group); + } + + var factory = new TagFactory(); + + return Database.Fetch(sql).Select(factory.BuildEntity); + } + + /// + /// Assigns the given tags to a content item's property + /// + /// + /// + /// The tags to assign + /// + /// If set to true, this will replace all tags with the given tags, + /// if false this will append the tags that already exist for the content item + /// + /// + /// + /// This can also be used to remove all tags from a property by specifying replaceTags = true and an empty tag list. + /// + public void AssignTagsToProperty(int contentId, string propertyTypeAlias, IEnumerable tags, bool replaceTags) + { + //First we need to ensure there are no duplicates + var asArray = tags.Distinct(new TagComparer()).ToArray(); + + //we don't have to do anything if there are no tags assigning and we're not replacing them + if (asArray.Length == 0 && replaceTags == false) + { + return; + } + + var propertyTypeId = EnsureContentProperty(contentId, propertyTypeAlias); + + //next check if we're removing all of the tags + if (asArray.Length == 0 && replaceTags) + { + Database.Execute("DELETE FROM cmsTagRelationship WHERE nodeId=" + contentId + " AND propertyTypeId=" + propertyTypeId); + return; + } + + //ok so now we're actually assigning tags... + //NOTE: There's some very clever logic in the umbraco.cms.businesslogic.Tags.Tag to insert tags where they don't exist, + // and assign where they don't exist which we've borrowed here. The queries are pretty zany but work, otherwise we'll end up + // with quite a few additional queries. + + //do all this in one transaction + using (var trans = Database.GetTransaction()) + { + //var factory = new TagFactory(); + + var tagSetSql = GetTagSet(asArray); + + //adds any tags found in the collection that aren't in cmsTag + var insertTagsSql = string.Concat("insert into cmsTags (Tag,", + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Group"), + ") ", + " select TagSet.Tag, TagSet.", + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Group"), + " from ", + tagSetSql, + " left outer join cmsTags on (TagSet.Tag = cmsTags.Tag and TagSet.", + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Group"), + " = cmsTags.", + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Group"), + ")", + " where cmsTags.Id is null "); + //insert the tags that don't exist + Database.Execute(insertTagsSql); + + if (replaceTags) + { + //if we are replacing the tags then remove them first + Database.Execute("DELETE FROM cmsTagRelationship WHERE nodeId=" + contentId + " AND propertyTypeId=" + propertyTypeId); + } + + //adds any tags found in csv that aren't in tagrelationships + var insertTagRelationsSql = string.Concat("insert into cmsTagRelationship (tagId,nodeId,propertyTypeId) ", + "select NewTagsSet.Id, " + contentId + ", " + propertyTypeId + " from ", + "( ", + "select NewTags.Id from ", + tagSetSql, + " inner join cmsTags as NewTags on (TagSet.Tag = NewTags.Tag and TagSet.", + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Group"), + " = TagSet.", + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Group"), + ") ", + ") as NewTagsSet ", + "left outer join cmsTagRelationship ", + "on (cmsTagRelationship.TagId = NewTagsSet.Id and cmsTagRelationship.nodeId = ", + contentId, + "and cmsTagRelationship.propertyTypeId = ", + propertyTypeId, + ") ", + "where cmsTagRelationship.tagId is null "); + + //insert the tags relations that don't exist + Database.Execute(insertTagRelationsSql); + + //GO! + trans.Complete(); + } + } + + /// + /// Removes any of the given tags from the property association + /// + /// + /// + /// The tags to remove from the property + public void RemoveTagsFromProperty(int contentId, string propertyTypeAlias, IEnumerable tags) + { + var propertyTypeId = EnsureContentProperty(contentId, propertyTypeAlias); + + var tagSetSql = GetTagSet(tags); + + var deleteSql = string.Concat("DELETE FROM cmsTagRelationship WHERE nodeId = ", + contentId, + " AND propertyTypeId = ", + propertyTypeId, + " AND tagId IN ", + "(SELECT id FROM cmsTags INNER JOIN ", + tagSetSql, + " ON (TagSet.Tag = cmsTags.Tag and TagSet.[Group] = cmsTags.[Group]))"); + + Database.Execute(deleteSql); + } + + private Guid GetNodeObjectType(TaggableObjectTypes type) + { + switch (type) + { + case TaggableObjectTypes.Content: + return new Guid(Constants.ObjectTypes.Document); + case TaggableObjectTypes.Media: + return new Guid(Constants.ObjectTypes.Media); + case TaggableObjectTypes.Member: + return new Guid(Constants.ObjectTypes.Member); + default: + throw new ArgumentOutOfRangeException("type"); + } + } + + /// + /// This is a clever way to produce an SQL statement like this: + /// + /// (select 'Spacesdd' as Tag, 'default' as [Group] + /// union + /// select 'Cool' as Tag, 'default' as [Group] + /// ) as TagSet + /// + /// This allows us to use the tags to be inserted as a temporary in memory table. + /// + /// + /// + private static string GetTagSet(IEnumerable tagsToInsert) + { + var array = tagsToInsert.Select(tag => string.Format("select '{0}' as Tag, '{1}' as [Group]", tag.Text, tag.Group)).ToArray(); + return "(" + string.Join(" union ", array).Replace(" ", " ") + ") as TagSet"; + } + + private class TagComparer : IEqualityComparer + { + public bool Equals(ITag x, ITag y) + { + return x.Text == y.Text && x.Group == y.Group; + } + + public int GetHashCode(ITag obj) + { + unchecked + { + return (obj.Text.GetHashCode() * 397) ^ obj.Group.GetHashCode(); + } + } + } + + /// + /// Ensures the content and property alias exist, then returns the property type id for the alias + /// + /// + /// + /// + private int EnsureContentProperty(int contentId, string propertyAlias) + { + //ensure that there's content and a property to assign - NOTE: we cannot use the content repository here + // because the content repository requires one of us TagsRepository instance, then we'll have circular dependencies + // instead we'll just look it up ourselves. + + var sql = new Sql() + .Select("cmsPropertyType.id") + .From() + .InnerJoin() + .On(left => left.NodeId, right => right.ContentTypeId) + .InnerJoin() + .On(left => left.ContentTypeId, right => right.NodeId) + .Where(dto => dto.NodeId == contentId) + .Where(dto => dto.Alias == propertyAlias); + + var result = Database.Fetch(sql).ToArray(); + + if (result.Length == 0) + { + throw new InvalidOperationException("Cannot modify tags for content id " + contentId + " and property " + propertyAlias + " because the content item does not exist with this property"); + } + + return result.First(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Profiling/ProfilingView.cs b/src/Umbraco.Core/Profiling/ProfilingView.cs index 110ab14925..354132df0f 100644 --- a/src/Umbraco.Core/Profiling/ProfilingView.cs +++ b/src/Umbraco.Core/Profiling/ProfilingView.cs @@ -1,28 +1,28 @@ -using System.IO; -using System.Web.Mvc; - -namespace Umbraco.Core.Profiling -{ - public class ProfilingView : IView - { - private readonly IView _inner; - private readonly string _name; - private readonly string _viewPath; - - public ProfilingView(IView inner) - { - _inner = inner; - _name = inner.GetType().Name; - var razorView = inner as RazorView; - _viewPath = razorView != null ? razorView.ViewPath : "Unknown"; - } - - public void Render(ViewContext viewContext, TextWriter writer) - { - using (ProfilerResolver.Current.Profiler.Step(string.Format("{0}.Render: {1}", _name, _viewPath))) - { - _inner.Render(viewContext, writer); - } - } - } +using System.IO; +using System.Web.Mvc; + +namespace Umbraco.Core.Profiling +{ + public class ProfilingView : IView + { + private readonly IView _inner; + private readonly string _name; + private readonly string _viewPath; + + public ProfilingView(IView inner) + { + _inner = inner; + _name = inner.GetType().Name; + var razorView = inner as RazorView; + _viewPath = razorView != null ? razorView.ViewPath : "Unknown"; + } + + public void Render(ViewContext viewContext, TextWriter writer) + { + using (ProfilerResolver.Current.Profiler.Step(string.Format("{0}.Render: {1}", _name, _viewPath))) + { + _inner.Render(viewContext, writer); + } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Profiling/ProfilingViewEngine.cs b/src/Umbraco.Core/Profiling/ProfilingViewEngine.cs index 5df2b6f811..641e060d50 100644 --- a/src/Umbraco.Core/Profiling/ProfilingViewEngine.cs +++ b/src/Umbraco.Core/Profiling/ProfilingViewEngine.cs @@ -1,48 +1,48 @@ -using System.Web.Mvc; - -namespace Umbraco.Core.Profiling -{ - public class ProfilingViewEngine: IViewEngine - { - private readonly IViewEngine _inner; - private readonly string _name; - - public ProfilingViewEngine(IViewEngine inner) - { - _inner = inner; - _name = inner.GetType().Name; - } - - public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) - { - using (ProfilerResolver.Current.Profiler.Step(string.Format("{0}.FindPartialView, {1}, {2}", _name, partialViewName, useCache))) - { - return WrapResult(_inner.FindPartialView(controllerContext, partialViewName, useCache)); - } - } - - public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) - { - using (ProfilerResolver.Current.Profiler.Step(string.Format("{0}.FindView, {1}, {2}, {3}", _name, viewName, masterName, useCache))) - { - return WrapResult(_inner.FindView(controllerContext, viewName, masterName, useCache)); - } - } - - private static ViewEngineResult WrapResult(ViewEngineResult innerResult) - { - var profiledResult = innerResult.View != null ? - new ViewEngineResult(new ProfilingView(innerResult.View), innerResult.ViewEngine) : - new ViewEngineResult(innerResult.SearchedLocations); - return profiledResult; - } - - public void ReleaseView(ControllerContext controllerContext, IView view) - { - using (ProfilerResolver.Current.Profiler.Step(string.Format("{0}.ReleaseView, {1}", _name, view.GetType().Name))) - { - _inner.ReleaseView(controllerContext, view); - } - } - } +using System.Web.Mvc; + +namespace Umbraco.Core.Profiling +{ + public class ProfilingViewEngine: IViewEngine + { + private readonly IViewEngine _inner; + private readonly string _name; + + public ProfilingViewEngine(IViewEngine inner) + { + _inner = inner; + _name = inner.GetType().Name; + } + + public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) + { + using (ProfilerResolver.Current.Profiler.Step(string.Format("{0}.FindPartialView, {1}, {2}", _name, partialViewName, useCache))) + { + return WrapResult(_inner.FindPartialView(controllerContext, partialViewName, useCache)); + } + } + + public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) + { + using (ProfilerResolver.Current.Profiler.Step(string.Format("{0}.FindView, {1}, {2}, {3}", _name, viewName, masterName, useCache))) + { + return WrapResult(_inner.FindView(controllerContext, viewName, masterName, useCache)); + } + } + + private static ViewEngineResult WrapResult(ViewEngineResult innerResult) + { + var profiledResult = innerResult.View != null ? + new ViewEngineResult(new ProfilingView(innerResult.View), innerResult.ViewEngine) : + new ViewEngineResult(innerResult.SearchedLocations); + return profiledResult; + } + + public void ReleaseView(ControllerContext controllerContext, IView view) + { + using (ProfilerResolver.Current.Profiler.Step(string.Format("{0}.ReleaseView, {1}", _name, view.GetType().Name))) + { + _inner.ReleaseView(controllerContext, view); + } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleData.cs b/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleData.cs index 102a2fc7f7..e9f426c323 100644 --- a/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleData.cs +++ b/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleData.cs @@ -1,69 +1,69 @@ -using System; -using System.Xml; -using Umbraco.Core.Models; -using umbraco.interfaces; - -namespace Umbraco.Core.PropertyEditors -{ - - /// - /// This is used purelty to attempt to maintain some backwards compatibility with new property editors that don't have a - /// legacy property editor predecessor when developers are using the legacy APIs - /// - internal class BackwardsCompatibleData : IData - { - private readonly string _propertyEditorAlias; - - public BackwardsCompatibleData(string propertyEditorAlias) - { - _propertyEditorAlias = propertyEditorAlias; - } - - public int PropertyId { set; get; } - - public object Value { get; set; } - - - public XmlNode ToXMl(XmlDocument data) - { - //we'll get the property editor by alias, if it exists (which it absolutely should), then we'll have to create a - // fake 'Property' object and pass it to the ConvertDbToXml method so we can get the correct XML fragment that - // it needs to make. - var propertyEditor = PropertyEditorResolver.Current.GetByAlias(_propertyEditorAlias); - if (propertyEditor != null) - { - //create a 'fake' property - we will never know the actual db type here so we'll just make it nvarchar, this shouldn't - // make any difference for the conversion process though. - var property = new Property(new PropertyType(_propertyEditorAlias, DataTypeDatabaseType.Nvarchar)) - { - Id = PropertyId, - Value = Value - }; - var xd = new XmlDocument(); - var xml = propertyEditor.ValueEditor.ConvertDbToXml(property); - xml.GetXmlNode(xd); - - // return the XML node. - return data.ImportNode(xd.DocumentElement, true); - } - - //if for some reason the prop editor wasn't found we'll default to returning the string value in a CDATA block. - var sValue = Value != null ? Value.ToString() : String.Empty; - return data.CreateCDataSection(sValue); - - } - - public void MakeNew(int PropertyId) - { - //DO nothing - } - - public void Delete() - { - throw new NotSupportedException( - typeof(IData) - + " is a legacy object and is not supported by runtime generated " - + " instances to maintain backwards compatibility with the legacy APIs. Consider upgrading your code to use the new Services APIs."); - } - } +using System; +using System.Xml; +using Umbraco.Core.Models; +using umbraco.interfaces; + +namespace Umbraco.Core.PropertyEditors +{ + + /// + /// This is used purelty to attempt to maintain some backwards compatibility with new property editors that don't have a + /// legacy property editor predecessor when developers are using the legacy APIs + /// + internal class BackwardsCompatibleData : IData + { + private readonly string _propertyEditorAlias; + + public BackwardsCompatibleData(string propertyEditorAlias) + { + _propertyEditorAlias = propertyEditorAlias; + } + + public int PropertyId { set; get; } + + public object Value { get; set; } + + + public XmlNode ToXMl(XmlDocument data) + { + //we'll get the property editor by alias, if it exists (which it absolutely should), then we'll have to create a + // fake 'Property' object and pass it to the ConvertDbToXml method so we can get the correct XML fragment that + // it needs to make. + var propertyEditor = PropertyEditorResolver.Current.GetByAlias(_propertyEditorAlias); + if (propertyEditor != null) + { + //create a 'fake' property - we will never know the actual db type here so we'll just make it nvarchar, this shouldn't + // make any difference for the conversion process though. + var property = new Property(new PropertyType(_propertyEditorAlias, DataTypeDatabaseType.Nvarchar)) + { + Id = PropertyId, + Value = Value + }; + var xd = new XmlDocument(); + var xml = propertyEditor.ValueEditor.ConvertDbToXml(property); + xml.GetXmlNode(xd); + + // return the XML node. + return data.ImportNode(xd.DocumentElement, true); + } + + //if for some reason the prop editor wasn't found we'll default to returning the string value in a CDATA block. + var sValue = Value != null ? Value.ToString() : String.Empty; + return data.CreateCDataSection(sValue); + + } + + public void MakeNew(int PropertyId) + { + //DO nothing + } + + public void Delete() + { + throw new NotSupportedException( + typeof(IData) + + " is a legacy object and is not supported by runtime generated " + + " instances to maintain backwards compatibility with the legacy APIs. Consider upgrading your code to use the new Services APIs."); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleDataType.cs b/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleDataType.cs index fe3dff4b82..db44925aac 100644 --- a/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleDataType.cs +++ b/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleDataType.cs @@ -1,62 +1,62 @@ -using System; -using umbraco.interfaces; - -namespace Umbraco.Core.PropertyEditors -{ - - /// - /// This is used purelty to attempt to maintain some backwards compatibility with new property editors that don't have a - /// legacy property editor predecessor when developers are using the legacy APIs - /// - internal class BackwardsCompatibleDataType : IDataType - { - public Guid Id { get; private set; } - public string DataTypeName { get; private set; } - public IData Data { get; private set; } - public int DataTypeDefinitionId { get; set; } - - /// - /// Creates a runtime instance - /// - /// - /// - /// - /// - internal static BackwardsCompatibleDataType Create(string propEdAlias, Guid legacyId, int dataTypeDefId) - { - var dt = new BackwardsCompatibleDataType - { - Id = legacyId, - DataTypeName = propEdAlias, - DataTypeDefinitionId = dataTypeDefId, - Data = new BackwardsCompatibleData(propEdAlias) - }; - - return dt; - } - - public IDataEditor DataEditor - { - get - { - throw new NotSupportedException( - typeof(IDataEditor) - + " is a legacy object and is not supported by runtime generated " - + typeof(IDataType) - + " instances to maintain backwards compatibility with the legacy APIs. Consider upgrading your code to use the new Services APIs."); - } - } - public IDataPrevalue PrevalueEditor - { - get - { - throw new NotSupportedException( - typeof(IDataPrevalue) - + " is a legacy object and is not supported by runtime generated " - + typeof(IDataType) - + " instances to maintain backwards compatibility with the legacy APIs. Consider upgrading your code to use the new Services APIs."); - } - } - - } +using System; +using umbraco.interfaces; + +namespace Umbraco.Core.PropertyEditors +{ + + /// + /// This is used purelty to attempt to maintain some backwards compatibility with new property editors that don't have a + /// legacy property editor predecessor when developers are using the legacy APIs + /// + internal class BackwardsCompatibleDataType : IDataType + { + public Guid Id { get; private set; } + public string DataTypeName { get; private set; } + public IData Data { get; private set; } + public int DataTypeDefinitionId { get; set; } + + /// + /// Creates a runtime instance + /// + /// + /// + /// + /// + internal static BackwardsCompatibleDataType Create(string propEdAlias, Guid legacyId, int dataTypeDefId) + { + var dt = new BackwardsCompatibleDataType + { + Id = legacyId, + DataTypeName = propEdAlias, + DataTypeDefinitionId = dataTypeDefId, + Data = new BackwardsCompatibleData(propEdAlias) + }; + + return dt; + } + + public IDataEditor DataEditor + { + get + { + throw new NotSupportedException( + typeof(IDataEditor) + + " is a legacy object and is not supported by runtime generated " + + typeof(IDataType) + + " instances to maintain backwards compatibility with the legacy APIs. Consider upgrading your code to use the new Services APIs."); + } + } + public IDataPrevalue PrevalueEditor + { + get + { + throw new NotSupportedException( + typeof(IDataPrevalue) + + " is a legacy object and is not supported by runtime generated " + + typeof(IDataType) + + " instances to maintain backwards compatibility with the legacy APIs. Consider upgrading your code to use the new Services APIs."); + } + } + + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/EmailValidator.cs b/src/Umbraco.Core/PropertyEditors/EmailValidator.cs index 0fb6a227be..0cda202756 100644 --- a/src/Umbraco.Core/PropertyEditors/EmailValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/EmailValidator.cs @@ -1,31 +1,31 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using Umbraco.Core.Models; - -namespace Umbraco.Core.PropertyEditors -{ - /// - /// A validator that validates an email address - /// - [ValueValidator("Email")] - internal sealed class EmailValidator : ManifestValueValidator, IPropertyValidator - { - public override IEnumerable Validate(object value, string config, PreValueCollection preValues, PropertyEditor editor) - { - var asString = value.ToString(); - - var emailVal = new EmailAddressAttribute(); - - if (emailVal.IsValid(asString) == false) - { - //TODO: localize these! - yield return new ValidationResult("Email is invalid", new[] { "value" }); - } - } - - public IEnumerable Validate(object value, PreValueCollection preValues, PropertyEditor editor) - { - return Validate(value, null, preValues, editor); - } - } +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Umbraco.Core.Models; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// A validator that validates an email address + /// + [ValueValidator("Email")] + internal sealed class EmailValidator : ManifestValueValidator, IPropertyValidator + { + public override IEnumerable Validate(object value, string config, PreValueCollection preValues, PropertyEditor editor) + { + var asString = value.ToString(); + + var emailVal = new EmailAddressAttribute(); + + if (emailVal.IsValid(asString) == false) + { + //TODO: localize these! + yield return new ValidationResult("Email is invalid", new[] { "value" }); + } + } + + public IEnumerable Validate(object value, PreValueCollection preValues, PropertyEditor editor) + { + return Validate(value, null, preValues, editor); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/IParameterEditor.cs b/src/Umbraco.Core/PropertyEditors/IParameterEditor.cs index cfa7174aae..44320c9464 100644 --- a/src/Umbraco.Core/PropertyEditors/IParameterEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/IParameterEditor.cs @@ -1,24 +1,24 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.PropertyEditors -{ - public interface IParameterEditor - { - /// - /// The id of the property editor - /// - string Alias { get; } - - /// - /// The name of the property editor - /// - string Name { get; } - - /// - /// Allows a parameter editor to be re-used based on the configuration specified. - /// - IDictionary Configuration { get; } - - IValueEditor ValueEditor { get; } - } +using System.Collections.Generic; + +namespace Umbraco.Core.PropertyEditors +{ + public interface IParameterEditor + { + /// + /// The id of the property editor + /// + string Alias { get; } + + /// + /// The name of the property editor + /// + string Name { get; } + + /// + /// Allows a parameter editor to be re-used based on the configuration specified. + /// + IDictionary Configuration { get; } + + IValueEditor ValueEditor { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyValueConverter.cs b/src/Umbraco.Core/PropertyEditors/IPropertyValueConverter.cs index 41d7dc7f7e..79887843a0 100644 --- a/src/Umbraco.Core/PropertyEditors/IPropertyValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/IPropertyValueConverter.cs @@ -1,68 +1,68 @@ -using Umbraco.Core.Models.PublishedContent; - -namespace Umbraco.Core.PropertyEditors -{ - /// - /// Provides published content properties conversion service. - /// - public interface IPropertyValueConverter - { - /// - /// Gets a value indicating whether the converter supports a property type. - /// - /// The property type. - /// A value indicating whether the converter supports a property type. - bool IsConverter(PublishedPropertyType propertyType); - - /// - /// Converts a property Data value to a Source value. - /// - /// The property type. - /// The data value. - /// A value indicating whether conversion should take place in preview mode. - /// The result of the conversion. - /// - /// The converter should know how to convert a null raw value, meaning that no - /// value has been assigned to the property. The source value can be null. - /// With the XML cache, raw values come from the XML cache and therefore are strings. - /// With objects caches, raw values would come from the database and therefore be either - /// ints, DateTimes, or strings. - /// The converter should be prepared to handle both situations. - /// When raw values are strings, the converter must handle empty strings, whitespace - /// strings, and xml-whitespace strings appropriately, ie it should know whether to preserve - /// whitespaces. - /// - object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview); - - /// - /// Converts a property Source value to an Object value. - /// - /// The property type. - /// The source value. - /// A value indicating whether conversion should take place in preview mode. - /// The result of the conversion. - /// The converter should know how to convert a null source value, or any source value - /// indicating that no value has been assigned to the property. It is up to the converter to determine - /// what to return in that case: either null, or the default value... - object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview); - - /// - /// Converts a property Source value to an XPath value. - /// - /// The property type. - /// The source value. - /// A value indicating whether conversion should take place in preview mode. - /// The result of the conversion. - /// - /// The converter should know how to convert a null source value, or any source value - /// indicating that no value has been assigned to the property. It is up to the converter to determine - /// what to return in that case: either null, or the default value... - /// If successful, the result should be either null, a string, or an XPathNavigator - /// instance. Whether an xml-whitespace string should be returned as null or litterally, is - /// up to the converter. - /// The converter may want to return an XML fragment that represent a part of the content tree, - /// but should pay attention not to create infinite loops that would kill XPath and XSLT. - /// - object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview); - } -} +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Provides published content properties conversion service. + /// + public interface IPropertyValueConverter + { + /// + /// Gets a value indicating whether the converter supports a property type. + /// + /// The property type. + /// A value indicating whether the converter supports a property type. + bool IsConverter(PublishedPropertyType propertyType); + + /// + /// Converts a property Data value to a Source value. + /// + /// The property type. + /// The data value. + /// A value indicating whether conversion should take place in preview mode. + /// The result of the conversion. + /// + /// The converter should know how to convert a null raw value, meaning that no + /// value has been assigned to the property. The source value can be null. + /// With the XML cache, raw values come from the XML cache and therefore are strings. + /// With objects caches, raw values would come from the database and therefore be either + /// ints, DateTimes, or strings. + /// The converter should be prepared to handle both situations. + /// When raw values are strings, the converter must handle empty strings, whitespace + /// strings, and xml-whitespace strings appropriately, ie it should know whether to preserve + /// whitespaces. + /// + object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview); + + /// + /// Converts a property Source value to an Object value. + /// + /// The property type. + /// The source value. + /// A value indicating whether conversion should take place in preview mode. + /// The result of the conversion. + /// The converter should know how to convert a null source value, or any source value + /// indicating that no value has been assigned to the property. It is up to the converter to determine + /// what to return in that case: either null, or the default value... + object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview); + + /// + /// Converts a property Source value to an XPath value. + /// + /// The property type. + /// The source value. + /// A value indicating whether conversion should take place in preview mode. + /// The result of the conversion. + /// + /// The converter should know how to convert a null source value, or any source value + /// indicating that no value has been assigned to the property. It is up to the converter to determine + /// what to return in that case: either null, or the default value... + /// If successful, the result should be either null, a string, or an XPathNavigator + /// instance. Whether an xml-whitespace string should be returned as null or litterally, is + /// up to the converter. + /// The converter may want to return an XML fragment that represent a part of the content tree, + /// but should pay attention not to create infinite loops that would kill XPath and XSLT. + /// + object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview); + } +} diff --git a/src/Umbraco.Core/PropertyEditors/IValueEditor.cs b/src/Umbraco.Core/PropertyEditors/IValueEditor.cs index 4b74f1d6d9..e084e468c9 100644 --- a/src/Umbraco.Core/PropertyEditors/IValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/IValueEditor.cs @@ -1,10 +1,10 @@ -namespace Umbraco.Core.PropertyEditors -{ - /// - /// An interface that is shared between parameter and property value editors to access their views - /// - public interface IValueEditor - { - string View { get; } - } +namespace Umbraco.Core.PropertyEditors +{ + /// + /// An interface that is shared between parameter and property value editors to access their views + /// + public interface IValueEditor + { + string View { get; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/LegacyParameterEditorAliasConverter.cs b/src/Umbraco.Core/PropertyEditors/LegacyParameterEditorAliasConverter.cs index 60ef70e4b8..41a0b439d8 100644 --- a/src/Umbraco.Core/PropertyEditors/LegacyParameterEditorAliasConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/LegacyParameterEditorAliasConverter.cs @@ -1,106 +1,106 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Data; -using System.Linq; - -namespace Umbraco.Core.PropertyEditors -{ - /// - /// Used to map the legacy parameter editor aliases to the new ones, this is really just used during - /// installation but has been put in a separate class in case we need it for other purposes - /// - internal static class LegacyParameterEditorAliasConverter - { - /// - /// The map consists of a key which is always the legacy alias + new alias (trimmed)) - /// - private static ConcurrentDictionary> _map = new ConcurrentDictionary>(); - - /// - /// Creates a map for the specified legacy alias and property editor alias - /// - /// - /// - /// true if the map was created or false if it was already created - public static bool CreateMap(string legacyAlias, string alias) - { - var key = legacyAlias.ToLowerInvariant() + alias.Trim(); - return _map.TryAdd(key, new Tuple(legacyAlias, alias)); - } - - /// - /// Gets an alias based on the legacy alias - /// - /// - /// if set to true will throw an exception if the map isn't found - /// Returns the alias if found otherwise null if not found - public static string GetNewAliasFromLegacyAlias(string legacyAlias, bool throwIfNotFound = false) - { - var found = _map.FirstOrDefault(x => x.Value.Item1 == legacyAlias); - if (found.Equals(default(KeyValuePair>))) - { - if (throwIfNotFound) - { - throw new ObjectNotFoundException("Could not find a map for a property editor with a legacy alias of " + legacyAlias); - } - return null; - } - return found.Value.Item2; - } - - /// - /// Gets a legacy Id based on the alias - /// - /// - /// if set to true will throw an exception if the map isn't found - /// Returns the legacy GUID of a property editor if found, otherwise returns null - public static string GetLegacyAliasFromNewAlias(string alias, bool throwIfNotFound = false) - { - var found = _map.FirstOrDefault(x => x.Value.Item2 == alias); - if (found.Equals(default(KeyValuePair>))) - { - if (throwIfNotFound) - { - throw new ObjectNotFoundException("Could not find a map for a property editor with an alias of " + alias); - } - return null; - } - return found.Value.Item1; - } - - internal static int Count() - { - return _map.Count; - } - - internal static void Reset() - { - _map = new ConcurrentDictionary>(); - } - - /// - /// A method that should be called on startup to register the mappings for the internal core editors - /// - internal static void CreateMappingsForCoreEditors() - { - //All of these map to the content picker - CreateMap("contentSubs", Constants.PropertyEditors.ContentPickerAlias); - CreateMap("contentRandom", Constants.PropertyEditors.ContentPickerAlias); - CreateMap("contentPicker", Constants.PropertyEditors.ContentPickerAlias); - CreateMap("contentTree", Constants.PropertyEditors.ContentPickerAlias); - CreateMap("contentAll", Constants.PropertyEditors.ContentPickerAlias); - - CreateMap("textMultiLine", Constants.PropertyEditors.TextboxMultipleAlias); - CreateMap("text", Constants.PropertyEditors.TextboxAlias); - CreateMap("bool", Constants.PropertyEditors.TrueFalseAlias); - - CreateMap("mediaCurrent", Constants.PropertyEditors.MediaPickerAlias); - - CreateMap("number", Constants.PropertyEditors.IntegerAlias); - - - - } - } +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Data; +using System.Linq; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Used to map the legacy parameter editor aliases to the new ones, this is really just used during + /// installation but has been put in a separate class in case we need it for other purposes + /// + internal static class LegacyParameterEditorAliasConverter + { + /// + /// The map consists of a key which is always the legacy alias + new alias (trimmed)) + /// + private static ConcurrentDictionary> _map = new ConcurrentDictionary>(); + + /// + /// Creates a map for the specified legacy alias and property editor alias + /// + /// + /// + /// true if the map was created or false if it was already created + public static bool CreateMap(string legacyAlias, string alias) + { + var key = legacyAlias.ToLowerInvariant() + alias.Trim(); + return _map.TryAdd(key, new Tuple(legacyAlias, alias)); + } + + /// + /// Gets an alias based on the legacy alias + /// + /// + /// if set to true will throw an exception if the map isn't found + /// Returns the alias if found otherwise null if not found + public static string GetNewAliasFromLegacyAlias(string legacyAlias, bool throwIfNotFound = false) + { + var found = _map.FirstOrDefault(x => x.Value.Item1 == legacyAlias); + if (found.Equals(default(KeyValuePair>))) + { + if (throwIfNotFound) + { + throw new ObjectNotFoundException("Could not find a map for a property editor with a legacy alias of " + legacyAlias); + } + return null; + } + return found.Value.Item2; + } + + /// + /// Gets a legacy Id based on the alias + /// + /// + /// if set to true will throw an exception if the map isn't found + /// Returns the legacy GUID of a property editor if found, otherwise returns null + public static string GetLegacyAliasFromNewAlias(string alias, bool throwIfNotFound = false) + { + var found = _map.FirstOrDefault(x => x.Value.Item2 == alias); + if (found.Equals(default(KeyValuePair>))) + { + if (throwIfNotFound) + { + throw new ObjectNotFoundException("Could not find a map for a property editor with an alias of " + alias); + } + return null; + } + return found.Value.Item1; + } + + internal static int Count() + { + return _map.Count; + } + + internal static void Reset() + { + _map = new ConcurrentDictionary>(); + } + + /// + /// A method that should be called on startup to register the mappings for the internal core editors + /// + internal static void CreateMappingsForCoreEditors() + { + //All of these map to the content picker + CreateMap("contentSubs", Constants.PropertyEditors.ContentPickerAlias); + CreateMap("contentRandom", Constants.PropertyEditors.ContentPickerAlias); + CreateMap("contentPicker", Constants.PropertyEditors.ContentPickerAlias); + CreateMap("contentTree", Constants.PropertyEditors.ContentPickerAlias); + CreateMap("contentAll", Constants.PropertyEditors.ContentPickerAlias); + + CreateMap("textMultiLine", Constants.PropertyEditors.TextboxMultipleAlias); + CreateMap("text", Constants.PropertyEditors.TextboxAlias); + CreateMap("bool", Constants.PropertyEditors.TrueFalseAlias); + + CreateMap("mediaCurrent", Constants.PropertyEditors.MediaPickerAlias); + + CreateMap("number", Constants.PropertyEditors.IntegerAlias); + + + + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/LegacyPropertyEditorIdToAliasConverter.cs b/src/Umbraco.Core/PropertyEditors/LegacyPropertyEditorIdToAliasConverter.cs index 75144a5938..b85bb5b41a 100644 --- a/src/Umbraco.Core/PropertyEditors/LegacyPropertyEditorIdToAliasConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/LegacyPropertyEditorIdToAliasConverter.cs @@ -1,154 +1,154 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Data; -using System.Linq; -using Umbraco.Core.Logging; - -namespace Umbraco.Core.PropertyEditors -{ - /// - /// This is used to map old legacy property editor GUID's to the new Property Editor alias (string) format. - /// - /// - /// This can be used by developers on application startup to register a mapping from their old ids to their new aliases and vice-versa. - /// - public static class LegacyPropertyEditorIdToAliasConverter - { - - public enum NotFoundLegacyIdResponseBehavior - { - ThrowException, - ReturnNull, - GenerateId - } - - /// - /// The map consists of a key which is always the GUID (lowercase, no hyphens + alias (trimmed)) - /// - private static ConcurrentDictionary> _map = new ConcurrentDictionary>(); - - /// - /// Creates a map for the specified legacy id and property editor alias - /// - /// - /// - /// true if the map was created or false if it was already created - public static bool CreateMap(Guid legacyId, string alias) - { - var key = legacyId.ToString("N").ToLowerInvariant() + alias.Trim(); - return _map.TryAdd(key, new Tuple(legacyId, alias)); - } - - /// - /// Gets an alias based on the legacy ID - /// - /// - /// if set to true will throw an exception if the map isn't found - /// Returns the alias if found otherwise null if not found - public static string GetAliasFromLegacyId(Guid legacyId, bool throwIfNotFound = false) - { - var found = _map.FirstOrDefault(x => x.Value.Item1 == legacyId); - if (found.Equals(default(KeyValuePair>))) - { - if (throwIfNotFound) - { - throw new ObjectNotFoundException("Could not find a map for a property editor with a legacy id of " + legacyId + ". Consider using the new business logic APIs instead of the old obsoleted ones."); - } - return null; - } - return found.Value.Item2; - } - - /// - /// Gets a legacy Id based on the alias - /// - /// - /// - /// Returns the legacy GUID of a property editor if found, otherwise returns null - public static Guid? GetLegacyIdFromAlias(string alias, NotFoundLegacyIdResponseBehavior notFoundBehavior) - { - var found = _map.FirstOrDefault(x => x.Value.Item2 == alias); - if (found.Equals(default(KeyValuePair>))) - { - switch (notFoundBehavior) - { - case NotFoundLegacyIdResponseBehavior.ThrowException: - throw new ObjectNotFoundException("Could not find a map for a property editor with an alias of " + alias + ". Consider using the new business logic APIs instead of the old obsoleted ones."); - case NotFoundLegacyIdResponseBehavior.ReturnNull: - return null; - case NotFoundLegacyIdResponseBehavior.GenerateId: - var generated = alias.EncodeAsGuid(); - CreateMap(generated, alias); - - LogHelper.Warn(typeof(LegacyPropertyEditorIdToAliasConverter), "A legacy GUID id was generated for property editor " + alias + ". This occurs when the legacy APIs are used and done to attempt to maintain backwards compatibility. Consider upgrading all code to use the new Services APIs instead to avoid any potential issues."); - - return generated; - } - } - return found.Value.Item1; - } - - internal static int Count() - { - return _map.Count; - } - - internal static void Reset() - { - _map = new ConcurrentDictionary>(); - } - - /// - /// A method that should be called on startup to register the mappings for the internal core editors - /// - internal static void CreateMappingsForCoreEditors() - { - CreateMap(Guid.Parse(Constants.PropertyEditors.CheckBoxList), Constants.PropertyEditors.CheckBoxListAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.ColorPicker), Constants.PropertyEditors.ColorPickerAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.ContentPicker), Constants.PropertyEditors.ContentPickerAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.Date), Constants.PropertyEditors.DateAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.DateTime), Constants.PropertyEditors.DateTimeAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.DropDownList), Constants.PropertyEditors.DropDownListAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.DropDownListMultiple), Constants.PropertyEditors.DropDownListMultipleAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.DropdownlistMultiplePublishKeys), Constants.PropertyEditors.DropdownlistMultiplePublishKeysAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.DropdownlistPublishingKeys), Constants.PropertyEditors.DropdownlistPublishingKeysAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.FolderBrowser), Constants.PropertyEditors.FolderBrowserAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.Integer), Constants.PropertyEditors.IntegerAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.ListView), Constants.PropertyEditors.ListViewAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.MacroContainer), Constants.PropertyEditors.MacroContainerAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.MediaPicker), Constants.PropertyEditors.MediaPickerAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.MemberPicker), Constants.PropertyEditors.MemberPickerAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.MultiNodeTreePicker), Constants.PropertyEditors.MultiNodeTreePickerAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.MultipleTextstring), Constants.PropertyEditors.MultipleTextstringAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.NoEdit), Constants.PropertyEditors.NoEditAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.RadioButtonList), Constants.PropertyEditors.RadioButtonListAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.RelatedLinks), Constants.PropertyEditors.RelatedLinksAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.Slider), Constants.PropertyEditors.SliderAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.Tags), Constants.PropertyEditors.TagsAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.Textbox), Constants.PropertyEditors.TextboxAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.TextboxMultiple), Constants.PropertyEditors.TextboxMultipleAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.TinyMCEv3), Constants.PropertyEditors.TinyMCEv3Alias); - CreateMap(Guid.Parse(Constants.PropertyEditors.TrueFalse), Constants.PropertyEditors.TrueFalseAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.UserPicker), Constants.PropertyEditors.UserPickerAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.UploadField), Constants.PropertyEditors.UploadFieldAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.XPathCheckBoxList), Constants.PropertyEditors.XPathCheckBoxListAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.XPathDropDownList), Constants.PropertyEditors.XPathDropDownListAlias); - - //Being mapped to different editors - //TODO: Map this somewhere! - CreateMap(Guid.Parse(Constants.PropertyEditors.PickerRelations), Constants.PropertyEditors.PickerRelationsAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.UltimatePicker), Constants.PropertyEditors.ContentPickerAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.UltraSimpleEditor), Constants.PropertyEditors.MarkdownEditorAlias); - - //Not being converted - convert to label - CreateMap(Guid.Parse(Constants.PropertyEditors.DictionaryPicker), Constants.PropertyEditors.NoEditAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.UmbracoUserControlWrapper), Constants.PropertyEditors.NoEditAlias); - - //Not ready for v7.0! - will need to create for 7.1 - CreateMap(Guid.Parse(Constants.PropertyEditors.MacroContainer), Constants.PropertyEditors.NoEditAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.ImageCropper), Constants.PropertyEditors.NoEditAlias); - } - - } -} +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using Umbraco.Core.Logging; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// This is used to map old legacy property editor GUID's to the new Property Editor alias (string) format. + /// + /// + /// This can be used by developers on application startup to register a mapping from their old ids to their new aliases and vice-versa. + /// + public static class LegacyPropertyEditorIdToAliasConverter + { + + public enum NotFoundLegacyIdResponseBehavior + { + ThrowException, + ReturnNull, + GenerateId + } + + /// + /// The map consists of a key which is always the GUID (lowercase, no hyphens + alias (trimmed)) + /// + private static ConcurrentDictionary> _map = new ConcurrentDictionary>(); + + /// + /// Creates a map for the specified legacy id and property editor alias + /// + /// + /// + /// true if the map was created or false if it was already created + public static bool CreateMap(Guid legacyId, string alias) + { + var key = legacyId.ToString("N").ToLowerInvariant() + alias.Trim(); + return _map.TryAdd(key, new Tuple(legacyId, alias)); + } + + /// + /// Gets an alias based on the legacy ID + /// + /// + /// if set to true will throw an exception if the map isn't found + /// Returns the alias if found otherwise null if not found + public static string GetAliasFromLegacyId(Guid legacyId, bool throwIfNotFound = false) + { + var found = _map.FirstOrDefault(x => x.Value.Item1 == legacyId); + if (found.Equals(default(KeyValuePair>))) + { + if (throwIfNotFound) + { + throw new ObjectNotFoundException("Could not find a map for a property editor with a legacy id of " + legacyId + ". Consider using the new business logic APIs instead of the old obsoleted ones."); + } + return null; + } + return found.Value.Item2; + } + + /// + /// Gets a legacy Id based on the alias + /// + /// + /// + /// Returns the legacy GUID of a property editor if found, otherwise returns null + public static Guid? GetLegacyIdFromAlias(string alias, NotFoundLegacyIdResponseBehavior notFoundBehavior) + { + var found = _map.FirstOrDefault(x => x.Value.Item2 == alias); + if (found.Equals(default(KeyValuePair>))) + { + switch (notFoundBehavior) + { + case NotFoundLegacyIdResponseBehavior.ThrowException: + throw new ObjectNotFoundException("Could not find a map for a property editor with an alias of " + alias + ". Consider using the new business logic APIs instead of the old obsoleted ones."); + case NotFoundLegacyIdResponseBehavior.ReturnNull: + return null; + case NotFoundLegacyIdResponseBehavior.GenerateId: + var generated = alias.EncodeAsGuid(); + CreateMap(generated, alias); + + LogHelper.Warn(typeof(LegacyPropertyEditorIdToAliasConverter), "A legacy GUID id was generated for property editor " + alias + ". This occurs when the legacy APIs are used and done to attempt to maintain backwards compatibility. Consider upgrading all code to use the new Services APIs instead to avoid any potential issues."); + + return generated; + } + } + return found.Value.Item1; + } + + internal static int Count() + { + return _map.Count; + } + + internal static void Reset() + { + _map = new ConcurrentDictionary>(); + } + + /// + /// A method that should be called on startup to register the mappings for the internal core editors + /// + internal static void CreateMappingsForCoreEditors() + { + CreateMap(Guid.Parse(Constants.PropertyEditors.CheckBoxList), Constants.PropertyEditors.CheckBoxListAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.ColorPicker), Constants.PropertyEditors.ColorPickerAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.ContentPicker), Constants.PropertyEditors.ContentPickerAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.Date), Constants.PropertyEditors.DateAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.DateTime), Constants.PropertyEditors.DateTimeAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.DropDownList), Constants.PropertyEditors.DropDownListAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.DropDownListMultiple), Constants.PropertyEditors.DropDownListMultipleAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.DropdownlistMultiplePublishKeys), Constants.PropertyEditors.DropdownlistMultiplePublishKeysAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.DropdownlistPublishingKeys), Constants.PropertyEditors.DropdownlistPublishingKeysAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.FolderBrowser), Constants.PropertyEditors.FolderBrowserAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.Integer), Constants.PropertyEditors.IntegerAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.ListView), Constants.PropertyEditors.ListViewAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.MacroContainer), Constants.PropertyEditors.MacroContainerAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.MediaPicker), Constants.PropertyEditors.MediaPickerAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.MemberPicker), Constants.PropertyEditors.MemberPickerAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.MultiNodeTreePicker), Constants.PropertyEditors.MultiNodeTreePickerAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.MultipleTextstring), Constants.PropertyEditors.MultipleTextstringAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.NoEdit), Constants.PropertyEditors.NoEditAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.RadioButtonList), Constants.PropertyEditors.RadioButtonListAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.RelatedLinks), Constants.PropertyEditors.RelatedLinksAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.Slider), Constants.PropertyEditors.SliderAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.Tags), Constants.PropertyEditors.TagsAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.Textbox), Constants.PropertyEditors.TextboxAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.TextboxMultiple), Constants.PropertyEditors.TextboxMultipleAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.TinyMCEv3), Constants.PropertyEditors.TinyMCEv3Alias); + CreateMap(Guid.Parse(Constants.PropertyEditors.TrueFalse), Constants.PropertyEditors.TrueFalseAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.UserPicker), Constants.PropertyEditors.UserPickerAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.UploadField), Constants.PropertyEditors.UploadFieldAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.XPathCheckBoxList), Constants.PropertyEditors.XPathCheckBoxListAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.XPathDropDownList), Constants.PropertyEditors.XPathDropDownListAlias); + + //Being mapped to different editors + //TODO: Map this somewhere! + CreateMap(Guid.Parse(Constants.PropertyEditors.PickerRelations), Constants.PropertyEditors.PickerRelationsAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.UltimatePicker), Constants.PropertyEditors.ContentPickerAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.UltraSimpleEditor), Constants.PropertyEditors.MarkdownEditorAlias); + + //Not being converted - convert to label + CreateMap(Guid.Parse(Constants.PropertyEditors.DictionaryPicker), Constants.PropertyEditors.NoEditAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.UmbracoUserControlWrapper), Constants.PropertyEditors.NoEditAlias); + + //Not ready for v7.0! - will need to create for 7.1 + CreateMap(Guid.Parse(Constants.PropertyEditors.MacroContainer), Constants.PropertyEditors.NoEditAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.ImageCropper), Constants.PropertyEditors.NoEditAlias); + } + + } +} diff --git a/src/Umbraco.Core/PropertyEditors/ParameterEditor.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditor.cs index 44e6b68153..044691162d 100644 --- a/src/Umbraco.Core/PropertyEditors/ParameterEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ParameterEditor.cs @@ -1,96 +1,96 @@ -using System; -using System.Collections.Generic; -using Newtonsoft.Json; -using Umbraco.Core.IO; - -namespace Umbraco.Core.PropertyEditors -{ - /// - /// Basic definition of a macro parameter editor - /// - public class ParameterEditor : IParameterEditor - { - - private readonly ParameterEditorAttribute _attribute; - - /// - /// The constructor will setup the property editor based on the attribute if one is found - /// - public ParameterEditor() - { - Configuration = new Dictionary(); - //assign properties based on the attribute if it is found - _attribute = GetType().GetCustomAttribute(false); - if (_attribute != null) - { - //set the id/name from the attribute - Alias = _attribute.Alias; - Name = _attribute.Name; - } - } - - /// - /// These are assigned by default normally based on parameter editor attributes or manifest definitions, - /// developers have the chance to override CreateValueEditor if they don't want to use the pre-defined instance - /// - internal ParameterValueEditor ManifestDefinedParameterValueEditor = null; - - /// - /// The id of the property editor - /// - [JsonProperty("alias", Required = Required.Always)] - public string Alias { get; internal set; } - - /// - /// The name of the property editor - /// - [JsonProperty("name", Required = Required.Always)] - public string Name { get; internal set; } - - /// - /// Allows a parameter editor to be re-used based on the configuration specified. - /// - [JsonProperty("config")] - public IDictionary Configuration { get; set; } - - [JsonIgnore] - public ParameterValueEditor ValueEditor - { - get { return CreateValueEditor(); } - } - - [JsonIgnore] - IValueEditor IParameterEditor.ValueEditor - { - get { return ValueEditor; } - } - - /// - /// Creates a value editor instance - /// - /// - protected virtual ParameterValueEditor CreateValueEditor() - { - if (ManifestDefinedParameterValueEditor != null) - { - //detect if the view is a virtual path (in most cases, yes) then convert it - if (ManifestDefinedParameterValueEditor.View.StartsWith("~/")) - { - ManifestDefinedParameterValueEditor.View = IOHelper.ResolveUrl(ManifestDefinedParameterValueEditor.View); - } - return ManifestDefinedParameterValueEditor; - } - - //create a new editor - var editor = new ParameterValueEditor(); - - if (_attribute.EditorView.IsNullOrWhiteSpace()) - { - throw new NotImplementedException("This method must be implemented if a view is not explicitly set"); - } - - editor.View = _attribute.EditorView; - return editor; - } - } +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Umbraco.Core.IO; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Basic definition of a macro parameter editor + /// + public class ParameterEditor : IParameterEditor + { + + private readonly ParameterEditorAttribute _attribute; + + /// + /// The constructor will setup the property editor based on the attribute if one is found + /// + public ParameterEditor() + { + Configuration = new Dictionary(); + //assign properties based on the attribute if it is found + _attribute = GetType().GetCustomAttribute(false); + if (_attribute != null) + { + //set the id/name from the attribute + Alias = _attribute.Alias; + Name = _attribute.Name; + } + } + + /// + /// These are assigned by default normally based on parameter editor attributes or manifest definitions, + /// developers have the chance to override CreateValueEditor if they don't want to use the pre-defined instance + /// + internal ParameterValueEditor ManifestDefinedParameterValueEditor = null; + + /// + /// The id of the property editor + /// + [JsonProperty("alias", Required = Required.Always)] + public string Alias { get; internal set; } + + /// + /// The name of the property editor + /// + [JsonProperty("name", Required = Required.Always)] + public string Name { get; internal set; } + + /// + /// Allows a parameter editor to be re-used based on the configuration specified. + /// + [JsonProperty("config")] + public IDictionary Configuration { get; set; } + + [JsonIgnore] + public ParameterValueEditor ValueEditor + { + get { return CreateValueEditor(); } + } + + [JsonIgnore] + IValueEditor IParameterEditor.ValueEditor + { + get { return ValueEditor; } + } + + /// + /// Creates a value editor instance + /// + /// + protected virtual ParameterValueEditor CreateValueEditor() + { + if (ManifestDefinedParameterValueEditor != null) + { + //detect if the view is a virtual path (in most cases, yes) then convert it + if (ManifestDefinedParameterValueEditor.View.StartsWith("~/")) + { + ManifestDefinedParameterValueEditor.View = IOHelper.ResolveUrl(ManifestDefinedParameterValueEditor.View); + } + return ManifestDefinedParameterValueEditor; + } + + //create a new editor + var editor = new ParameterValueEditor(); + + if (_attribute.EditorView.IsNullOrWhiteSpace()) + { + throw new NotImplementedException("This method must be implemented if a view is not explicitly set"); + } + + editor.View = _attribute.EditorView; + return editor; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/ParameterEditorAttribute.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditorAttribute.cs index ab6fb559e4..b43cf9649c 100644 --- a/src/Umbraco.Core/PropertyEditors/ParameterEditorAttribute.cs +++ b/src/Umbraco.Core/PropertyEditors/ParameterEditorAttribute.cs @@ -1,37 +1,37 @@ -using System; - -namespace Umbraco.Core.PropertyEditors -{ - /// - /// An attribute used to define all of the basic properties of a parameter editor - /// on the server side. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public sealed class ParameterEditorAttribute : Attribute - { - public ParameterEditorAttribute(string alias, string name, string editorView) - { - Mandate.ParameterNotNullOrEmpty(alias, "alias"); - Mandate.ParameterNotNullOrEmpty(name, "name"); - Mandate.ParameterNotNullOrEmpty(editorView, "editorView"); - - Alias = alias; - Name = name; - EditorView = editorView; - } - - public ParameterEditorAttribute(string alias, string name) - { - Mandate.ParameterNotNullOrEmpty(alias, "id"); - Mandate.ParameterNotNullOrEmpty(name, "name"); - - Alias = alias; - Name = name; - } - - - public string Alias { get; private set; } - public string Name { get; private set; } - public string EditorView { get; private set; } - } +using System; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// An attribute used to define all of the basic properties of a parameter editor + /// on the server side. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class ParameterEditorAttribute : Attribute + { + public ParameterEditorAttribute(string alias, string name, string editorView) + { + Mandate.ParameterNotNullOrEmpty(alias, "alias"); + Mandate.ParameterNotNullOrEmpty(name, "name"); + Mandate.ParameterNotNullOrEmpty(editorView, "editorView"); + + Alias = alias; + Name = name; + EditorView = editorView; + } + + public ParameterEditorAttribute(string alias, string name) + { + Mandate.ParameterNotNullOrEmpty(alias, "id"); + Mandate.ParameterNotNullOrEmpty(name, "name"); + + Alias = alias; + Name = name; + } + + + public string Alias { get; private set; } + public string Name { get; private set; } + public string EditorView { get; private set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/ParameterEditorResolver.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditorResolver.cs index 787291a473..ef01b3a891 100644 --- a/src/Umbraco.Core/PropertyEditors/ParameterEditorResolver.cs +++ b/src/Umbraco.Core/PropertyEditors/ParameterEditorResolver.cs @@ -1,64 +1,64 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core.Manifest; -using Umbraco.Core.ObjectResolution; - -namespace Umbraco.Core.PropertyEditors -{ - /// - /// A resolver to resolve all parameter editors - /// - /// - /// This resolver will contain any parameter editors defined in manifests as well as any property editors defined in manifests - /// that have the IsParameterEditorFlag = true and any PropertyEditors found in c# that have this flag as well. - /// - internal class ParameterEditorResolver : LazyManyObjectsResolverBase - { - public ParameterEditorResolver(Func> typeListProducerList) - : base(typeListProducerList, ObjectLifetimeScope.Application) - { - } - - /// - /// Returns the parameter editors - /// - public IEnumerable ParameterEditors - { - get - { - //This will by default include all property editors and parameter editors but we need to filter this - //list to ensure that none of the property editors that do not have the IsParameterEditor flag set to true - //are filtered. - var filtered = Values.Select(x => x as PropertyEditor) - .WhereNotNull() - .Where(x => x.IsParameterEditor == false); - - return Values - //exclude the non parameter editor c# property editors - .Except(filtered) - //include the manifest parameter editors - .Union(ManifestBuilder.ParameterEditors) - //include the manifest prop editors that are parameter editors - .Union(ManifestBuilder.PropertyEditors.Where(x => x.IsParameterEditor)); - } - } - - /// - /// Returns a property editor by alias - /// - /// - /// - public IParameterEditor GetByAlias(string alias) - { - var found = ParameterEditors.SingleOrDefault(x => x.Alias == alias); - if (found != null) return found; - - //couldn't find one, so try the map - var mapped = LegacyParameterEditorAliasConverter.GetNewAliasFromLegacyAlias(alias); - return mapped == null - ? null - : ParameterEditors.SingleOrDefault(x => x.Alias == mapped); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Manifest; +using Umbraco.Core.ObjectResolution; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// A resolver to resolve all parameter editors + /// + /// + /// This resolver will contain any parameter editors defined in manifests as well as any property editors defined in manifests + /// that have the IsParameterEditorFlag = true and any PropertyEditors found in c# that have this flag as well. + /// + internal class ParameterEditorResolver : LazyManyObjectsResolverBase + { + public ParameterEditorResolver(Func> typeListProducerList) + : base(typeListProducerList, ObjectLifetimeScope.Application) + { + } + + /// + /// Returns the parameter editors + /// + public IEnumerable ParameterEditors + { + get + { + //This will by default include all property editors and parameter editors but we need to filter this + //list to ensure that none of the property editors that do not have the IsParameterEditor flag set to true + //are filtered. + var filtered = Values.Select(x => x as PropertyEditor) + .WhereNotNull() + .Where(x => x.IsParameterEditor == false); + + return Values + //exclude the non parameter editor c# property editors + .Except(filtered) + //include the manifest parameter editors + .Union(ManifestBuilder.ParameterEditors) + //include the manifest prop editors that are parameter editors + .Union(ManifestBuilder.PropertyEditors.Where(x => x.IsParameterEditor)); + } + } + + /// + /// Returns a property editor by alias + /// + /// + /// + public IParameterEditor GetByAlias(string alias) + { + var found = ParameterEditors.SingleOrDefault(x => x.Alias == alias); + if (found != null) return found; + + //couldn't find one, so try the map + var mapped = LegacyParameterEditorAliasConverter.GetNewAliasFromLegacyAlias(alias); + return mapped == null + ? null + : ParameterEditors.SingleOrDefault(x => x.Alias == mapped); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/ParameterValueEditor.cs b/src/Umbraco.Core/PropertyEditors/ParameterValueEditor.cs index 4ee84ab93a..6c5f03f9e7 100644 --- a/src/Umbraco.Core/PropertyEditors/ParameterValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ParameterValueEditor.cs @@ -1,29 +1,29 @@ -using Newtonsoft.Json; - -namespace Umbraco.Core.PropertyEditors -{ - /// - /// Represents the value editor for the parameter editor during macro parameter editing - /// - public class ParameterValueEditor : IValueEditor - { - /// - /// default ctor - /// - public ParameterValueEditor() - { - } - - /// - /// Creates a new editor with the specified view - /// - /// - public ParameterValueEditor(string view) - : this() - { - View = view; - } - - public string View { get; set; } - } +using Newtonsoft.Json; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Represents the value editor for the parameter editor during macro parameter editing + /// + public class ParameterValueEditor : IValueEditor + { + /// + /// default ctor + /// + public ParameterValueEditor() + { + } + + /// + /// Creates a new editor with the specified view + /// + /// + public ParameterValueEditor(string view) + : this() + { + View = view; + } + + public string View { get; set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/PropertyCacheLevel.cs b/src/Umbraco.Core/PropertyEditors/PropertyCacheLevel.cs index 1e365d1ac4..e271a01a5e 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyCacheLevel.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyCacheLevel.cs @@ -1,33 +1,33 @@ -namespace Umbraco.Core.PropertyEditors -{ - /// - /// Specifies the acceptable level of cache for a property value. - /// - /// By default, Request is assumed. - public enum PropertyCacheLevel - { - /// - /// Indicates that the property value can be cached at the content level, ie it can be - /// cached until the content itself is modified. - /// - Content = 1, - - /// - /// Indicates that the property value can be cached at the content cache level, ie it can - /// be cached until any content in the cache is modified. - /// - ContentCache = 2, - - /// - /// Indicates that the property value can be cached at the request level, ie it can be - /// cached for the duration of the current request. - /// - Request = 3, - - /// - /// Indicates that the property value cannot be cached and has to be converted any time - /// it is requested. - /// - None = 4 - } -} +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Specifies the acceptable level of cache for a property value. + /// + /// By default, Request is assumed. + public enum PropertyCacheLevel + { + /// + /// Indicates that the property value can be cached at the content level, ie it can be + /// cached until the content itself is modified. + /// + Content = 1, + + /// + /// Indicates that the property value can be cached at the content cache level, ie it can + /// be cached until any content in the cache is modified. + /// + ContentCache = 2, + + /// + /// Indicates that the property value can be cached at the request level, ie it can be + /// cached for the duration of the current request. + /// + Request = 3, + + /// + /// Indicates that the property value cannot be cached and has to be converted any time + /// it is requested. + /// + None = 4 + } +} diff --git a/src/Umbraco.Core/PropertyEditors/PropertyCacheValue.cs b/src/Umbraco.Core/PropertyEditors/PropertyCacheValue.cs index c4f438fb5e..cb90602b7d 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyCacheValue.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyCacheValue.cs @@ -1,29 +1,29 @@ -namespace Umbraco.Core.PropertyEditors -{ - /// - /// Specifies the different types of property cacheable values. - /// - public enum PropertyCacheValue - { - /// - /// All of them. - /// - All, - - /// - /// The source value ie the internal value that can be used to create both the - /// object value and the xpath value. - /// - Source, - - /// - /// The object value ie the strongly typed value of the property as seen when accessing content via C#. - /// - Object, - - /// - /// The XPath value ie the value of the property as seen when accessing content via XPath. - /// - XPath - } -} +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Specifies the different types of property cacheable values. + /// + public enum PropertyCacheValue + { + /// + /// All of them. + /// + All, + + /// + /// The source value ie the internal value that can be used to create both the + /// object value and the xpath value. + /// + Source, + + /// + /// The object value ie the strongly typed value of the property as seen when accessing content via C#. + /// + Object, + + /// + /// The XPath value ie the value of the property as seen when accessing content via XPath. + /// + XPath + } +} diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueCacheAttribute.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueCacheAttribute.cs index 76d16b79c6..51fab96648 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyValueCacheAttribute.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyValueCacheAttribute.cs @@ -1,34 +1,34 @@ -using System; -using log4net.Core; - -namespace Umbraco.Core.PropertyEditors -{ - /// - /// Indicates the cache level for a property cacheable value. - /// - /// Use this attribute to mark property values converters. - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] - public class PropertyValueCacheAttribute : Attribute - { - /// - /// Initializes a new instance of the class with a cacheable value and a cache level. - /// - /// The cacheable value. - /// The cache level. - public PropertyValueCacheAttribute(PropertyCacheValue value, PropertyCacheLevel level) - { - Value = value; - Level = level; - } - - /// - /// Gets or sets the cacheable value. - /// - public PropertyCacheValue Value { get; private set; } - - /// - /// Gets or sets the cache level; - /// - public PropertyCacheLevel Level { get; private set; } - } +using System; +using log4net.Core; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Indicates the cache level for a property cacheable value. + /// + /// Use this attribute to mark property values converters. + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] + public class PropertyValueCacheAttribute : Attribute + { + /// + /// Initializes a new instance of the class with a cacheable value and a cache level. + /// + /// The cacheable value. + /// The cache level. + public PropertyValueCacheAttribute(PropertyCacheValue value, PropertyCacheLevel level) + { + Value = value; + Level = level; + } + + /// + /// Gets or sets the cacheable value. + /// + public PropertyCacheValue Value { get; private set; } + + /// + /// Gets or sets the cache level; + /// + public PropertyCacheLevel Level { get; private set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs index cecba7d4a2..a124f0fcb2 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs @@ -1,30 +1,30 @@ -using Umbraco.Core.Models.PublishedContent; - -namespace Umbraco.Core.PropertyEditors -{ - /// - /// Provides a default overridable implementation for that does nothing. - /// - public class PropertyValueConverterBase : IPropertyValueConverter - { - public virtual bool IsConverter(PublishedPropertyType propertyType) - { - return false; - } - - public virtual object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) - { - return PublishedPropertyType.ConvertUsingDarkMagic(source); - } - - public virtual object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) - { - return source; - } - - public virtual object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview) - { - return source.ToString(); - } - } -} +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Provides a default overridable implementation for that does nothing. + /// + public class PropertyValueConverterBase : IPropertyValueConverter + { + public virtual bool IsConverter(PublishedPropertyType propertyType) + { + return false; + } + + public virtual object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + return PublishedPropertyType.ConvertUsingDarkMagic(source); + } + + public virtual object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) + { + return source; + } + + public virtual object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview) + { + return source.ToString(); + } + } +} diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs index af3b5cfcc7..53fecdac9d 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs @@ -1,40 +1,40 @@ -using System; -using System.Collections.Generic; -using Umbraco.Core.ObjectResolution; - -namespace Umbraco.Core.PropertyEditors -{ - /// - /// Resolves the IPropertyValueConverter objects. - /// - public sealed class PropertyValueConvertersResolver : ManyObjectsResolverBase - { - /// - /// Initializes a new instance of the class with - /// an initial list of converter types. - /// - /// The list of converter types - /// The resolver is created by the WebBootManager and thus the constructor remains internal. - internal PropertyValueConvertersResolver(IEnumerable converters) - : base(converters) - { } - - /// - /// Initializes a new instance of the class with - /// an initial list of converter types. - /// - /// The list of converter types - /// The resolver is created by the WebBootManager and thus the constructor remains internal. - internal PropertyValueConvertersResolver(params Type[] converters) - : base(converters) - { } - - /// - /// Gets the converters. - /// - public IEnumerable Converters - { - get { return Values; } - } - } +using System; +using System.Collections.Generic; +using Umbraco.Core.ObjectResolution; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Resolves the IPropertyValueConverter objects. + /// + public sealed class PropertyValueConvertersResolver : ManyObjectsResolverBase + { + /// + /// Initializes a new instance of the class with + /// an initial list of converter types. + /// + /// The list of converter types + /// The resolver is created by the WebBootManager and thus the constructor remains internal. + internal PropertyValueConvertersResolver(IEnumerable converters) + : base(converters) + { } + + /// + /// Initializes a new instance of the class with + /// an initial list of converter types. + /// + /// The list of converter types + /// The resolver is created by the WebBootManager and thus the constructor remains internal. + internal PropertyValueConvertersResolver(params Type[] converters) + : base(converters) + { } + + /// + /// Gets the converters. + /// + public IEnumerable Converters + { + get { return Values; } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs index 0ce00288c8..e004779b60 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs @@ -1,346 +1,346 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Xml.Linq; -using Newtonsoft.Json; -using Umbraco.Core.Logging; -using Umbraco.Core.Manifest; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Editors; - -namespace Umbraco.Core.PropertyEditors -{ - /// - /// Represents the value editor for the property editor during content editing - /// - /// - /// The Json serialization attributes are required for manifest property editors to work - /// - public class PropertyValueEditor : IValueEditor - { - /// - /// assign defaults - /// - public PropertyValueEditor() - { - ValueType = "string"; - //set a default for validators - Validators = new List(); - } - - /// - /// Creates a new editor with the specified view - /// - /// - /// Allows adding custom validators during construction instead of specifying them later - public PropertyValueEditor(string view, params IPropertyValidator[] validators) - : this() - { - View = view; - foreach (var v in validators) - { - Validators.Add(v); - } - } - - private PreValueCollection _preVals; - protected PreValueCollection PreValues - { - get - { - if (_preVals == null) - { - throw new InvalidOperationException("Pre values cannot be accessed until the Configure method has been called"); - } - return _preVals; - } - } - - /// - /// This is called to configure the editor for display with it's prevalues, useful when properties need to change dynamically - /// depending on what is in the pre-values. - /// - /// - /// - /// This cannot be used to change the value being sent to the editor, ConfigureEditor will be called *after* ConvertDbToEditor, pre-values - /// should not be used to modify values. - /// - public virtual void ConfigureForDisplay(PreValueCollection preValues) - { - if (preValues == null) throw new ArgumentNullException("preValues"); - _preVals = preValues; - } - - /// - /// Defines the view to use for the editor, this can be one of 3 things: - /// * the full virtual path or - /// * the relative path to the current Umbraco folder - /// * a simple view name which will map to the views/propertyeditors/{view}/{view}.html - /// - [JsonProperty("view", Required = Required.Always)] - public string View { get; set; } - - /// - /// The value type which reflects how it is validated and stored in the database - /// - [JsonProperty("valueType")] - public string ValueType { get; set; } - - /// - /// A collection of validators for the pre value editor - /// - [JsonProperty("validation", ItemConverterType = typeof(ManifestValidatorConverter))] - public List Validators { get; private set; } - - /// - /// Returns the validator used for the required field validation which is specified on the PropertyType - /// - /// - /// This will become legacy as soon as we implement overridable pre-values. - /// - /// The default validator used is the RequiredValueValidator but this can be overridden by property editors - /// if they need to do some custom validation, or if the value being validated is a json object. - /// - internal virtual ManifestValueValidator RequiredValidator - { - get { return new RequiredManifestValueValidator(); } - } - - /// - /// Returns the validator used for the regular expression field validation which is specified on the PropertyType - /// - /// - /// This will become legacy as soon as we implement overridable pre-values. - /// - /// The default validator used is the RegexValueValidator but this can be overridden by property editors - /// if they need to do some custom validation, or if the value being validated is a json object. - /// - internal virtual ManifestValueValidator RegexValidator - { - get { return new RegexValidator(); } - } - - /// - /// Returns the true DataTypeDatabaseType from the string representation ValueType - /// - /// - public DataTypeDatabaseType GetDatabaseType() - { - switch (ValueType.ToUpper()) - { - case "INT": - case "INTEGER": - return DataTypeDatabaseType.Integer; - case "STRING": - return DataTypeDatabaseType.Nvarchar; - case "TEXT": - case "JSON": - case "XML": - return DataTypeDatabaseType.Ntext; - case "DATETIME": - case "DATE": - case "TIME": - return DataTypeDatabaseType.Date; - default: - throw new FormatException("The ValueType does not match a known value type"); - } - } - - /// - /// If this is is true than the editor will be displayed full width without a label - /// - [JsonProperty("hideLabel")] - public bool HideLabel { get; set; } - - /// - /// Set this to true if the property editor is for display purposes only - /// - public virtual bool IsReadOnly - { - get { return false; } - } - - /// - /// Used to try to convert the string value to the correct CLR type based on the DatabaseDataType specified for this value editor - /// - /// - /// - internal Attempt TryConvertValueToCrlType(object value) - { - - //this is a custom check to avoid any errors, if it's a string and it's empty just make it null - var s = value as string; - if (s != null) - { - if (s.IsNullOrWhiteSpace()) - { - value = null; - } - } - - Type valueType; - //convert the string to a known type - switch (GetDatabaseType()) - { - case DataTypeDatabaseType.Ntext: - case DataTypeDatabaseType.Nvarchar: - valueType = typeof(string); - break; - case DataTypeDatabaseType.Integer: - //ensure these are nullable so we can return a null if required - //NOTE: This is allowing type of 'long' because I think json.net will deserialize a numerical value as long - // instead of int. Even though our db will not support this (will get truncated), we'll at least parse to this. - - valueType = typeof(long?); - break; - case DataTypeDatabaseType.Date: - //ensure these are nullable so we can return a null if required - valueType = typeof(DateTime?); - break; - default: - throw new ArgumentOutOfRangeException(); - } - return value.TryConvertTo(valueType); - } - - /// - /// A method to deserialize the string value that has been saved in the content editor - /// to an object to be stored in the database. - /// - /// - /// - /// The current value that has been persisted to the database for this editor. This value may be usesful for - /// how the value then get's deserialized again to be re-persisted. In most cases it will probably not be used. - /// - /// - /// - /// By default this will attempt to automatically convert the string value to the value type supplied by ValueType. - /// - /// If overridden then the object returned must match the type supplied in the ValueType, otherwise persisting the - /// value to the DB will fail when it tries to validate the value type. - /// - public virtual object ConvertEditorToDb(ContentPropertyData editorValue, object currentValue) - { - var result = TryConvertValueToCrlType(editorValue.Value); - if (result.Success == false) - { - LogHelper.Warn("The value " + editorValue.Value + " cannot be converted to the type " + GetDatabaseType()); - return null; - } - return result.Result; - } - - //TODO: Change the result to object so we can pass back JSON or json converted clr types if we want! - - /// - /// A method used to format the database value to a value that can be used by the editor - /// - /// - /// - /// - /// The object returned will automatically be serialized into json notation. For most property editors - /// the value returned is probably just a string but in some cases a json structure will be returned. - /// - public virtual object ConvertDbToEditor(object dbValue) - { - if (dbValue == null) return string.Empty; - - switch (GetDatabaseType()) - { - case DataTypeDatabaseType.Ntext: - case DataTypeDatabaseType.Nvarchar: - //if it is a string type, we will attempt to see if it is json stored data, if it is we'll try to convert - //to a real json object so we can pass the true json object directly to angular! - var asString = dbValue.ToString(); - if (asString.DetectIsJson()) - { - try - { - var json = JsonConvert.DeserializeObject(asString); - return json; - } - catch - { - //swallow this exception, we thought it was json but it really isn't so continue returning a string - } - } - return dbValue.ToString(); - case DataTypeDatabaseType.Integer: - //we can just ToString() any of these types - return dbValue.ToString(); - case DataTypeDatabaseType.Date: - var date = dbValue.TryConvertTo(); - if (date.Success == false || date.Result == null) - { - return string.Empty; - } - //Dates will be formatted as yyyy-MM-dd HH:mm:ss - return date.Result.Value.ToIsoString(); - default: - throw new ArgumentOutOfRangeException(); - } - } - - /// - /// Converts the property db value to an XML fragment - /// - /// - /// - /// - /// By default this will just return the value of ConvertDbToString but ensure that if the db value type is nvarchar or text - /// it is a CDATA fragment, otherwise it is just a text fragment. - /// - public virtual XNode ConvertDbToXml(Property property) - { - switch (GetDatabaseType()) - { - case DataTypeDatabaseType.Date: - case DataTypeDatabaseType.Integer: - return new XText(ConvertDbToString(property)); - case DataTypeDatabaseType.Nvarchar: - case DataTypeDatabaseType.Ntext: - //put text in cdata - return new XCData(ConvertDbToString(property)); - default: - throw new ArgumentOutOfRangeException(); - } - } - - /// - /// Converts the property value for use in the front-end cache - /// - /// - /// - public virtual string ConvertDbToString(Property property) - { - if (property.Value == null) - { - return string.Empty; - } - - switch (GetDatabaseType()) - { - case DataTypeDatabaseType.Nvarchar: - case DataTypeDatabaseType.Ntext: - property.Value.ToXmlString(); - return property.Value.ToXmlString(); - case DataTypeDatabaseType.Integer: - return property.Value.ToXmlString(property.Value.GetType()); - case DataTypeDatabaseType.Date: - //treat dates differently, output the format as xml format - if (property.Value == null) - { - return string.Empty; - } - var date = property.Value.TryConvertTo(); - if (date.Success == false || date.Result == null) - { - return string.Empty; - } - return date.Result.ToXmlString(); - default: - throw new ArgumentOutOfRangeException(); - } - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using Newtonsoft.Json; +using Umbraco.Core.Logging; +using Umbraco.Core.Manifest; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Editors; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Represents the value editor for the property editor during content editing + /// + /// + /// The Json serialization attributes are required for manifest property editors to work + /// + public class PropertyValueEditor : IValueEditor + { + /// + /// assign defaults + /// + public PropertyValueEditor() + { + ValueType = "string"; + //set a default for validators + Validators = new List(); + } + + /// + /// Creates a new editor with the specified view + /// + /// + /// Allows adding custom validators during construction instead of specifying them later + public PropertyValueEditor(string view, params IPropertyValidator[] validators) + : this() + { + View = view; + foreach (var v in validators) + { + Validators.Add(v); + } + } + + private PreValueCollection _preVals; + protected PreValueCollection PreValues + { + get + { + if (_preVals == null) + { + throw new InvalidOperationException("Pre values cannot be accessed until the Configure method has been called"); + } + return _preVals; + } + } + + /// + /// This is called to configure the editor for display with it's prevalues, useful when properties need to change dynamically + /// depending on what is in the pre-values. + /// + /// + /// + /// This cannot be used to change the value being sent to the editor, ConfigureEditor will be called *after* ConvertDbToEditor, pre-values + /// should not be used to modify values. + /// + public virtual void ConfigureForDisplay(PreValueCollection preValues) + { + if (preValues == null) throw new ArgumentNullException("preValues"); + _preVals = preValues; + } + + /// + /// Defines the view to use for the editor, this can be one of 3 things: + /// * the full virtual path or + /// * the relative path to the current Umbraco folder + /// * a simple view name which will map to the views/propertyeditors/{view}/{view}.html + /// + [JsonProperty("view", Required = Required.Always)] + public string View { get; set; } + + /// + /// The value type which reflects how it is validated and stored in the database + /// + [JsonProperty("valueType")] + public string ValueType { get; set; } + + /// + /// A collection of validators for the pre value editor + /// + [JsonProperty("validation", ItemConverterType = typeof(ManifestValidatorConverter))] + public List Validators { get; private set; } + + /// + /// Returns the validator used for the required field validation which is specified on the PropertyType + /// + /// + /// This will become legacy as soon as we implement overridable pre-values. + /// + /// The default validator used is the RequiredValueValidator but this can be overridden by property editors + /// if they need to do some custom validation, or if the value being validated is a json object. + /// + internal virtual ManifestValueValidator RequiredValidator + { + get { return new RequiredManifestValueValidator(); } + } + + /// + /// Returns the validator used for the regular expression field validation which is specified on the PropertyType + /// + /// + /// This will become legacy as soon as we implement overridable pre-values. + /// + /// The default validator used is the RegexValueValidator but this can be overridden by property editors + /// if they need to do some custom validation, or if the value being validated is a json object. + /// + internal virtual ManifestValueValidator RegexValidator + { + get { return new RegexValidator(); } + } + + /// + /// Returns the true DataTypeDatabaseType from the string representation ValueType + /// + /// + public DataTypeDatabaseType GetDatabaseType() + { + switch (ValueType.ToUpper()) + { + case "INT": + case "INTEGER": + return DataTypeDatabaseType.Integer; + case "STRING": + return DataTypeDatabaseType.Nvarchar; + case "TEXT": + case "JSON": + case "XML": + return DataTypeDatabaseType.Ntext; + case "DATETIME": + case "DATE": + case "TIME": + return DataTypeDatabaseType.Date; + default: + throw new FormatException("The ValueType does not match a known value type"); + } + } + + /// + /// If this is is true than the editor will be displayed full width without a label + /// + [JsonProperty("hideLabel")] + public bool HideLabel { get; set; } + + /// + /// Set this to true if the property editor is for display purposes only + /// + public virtual bool IsReadOnly + { + get { return false; } + } + + /// + /// Used to try to convert the string value to the correct CLR type based on the DatabaseDataType specified for this value editor + /// + /// + /// + internal Attempt TryConvertValueToCrlType(object value) + { + + //this is a custom check to avoid any errors, if it's a string and it's empty just make it null + var s = value as string; + if (s != null) + { + if (s.IsNullOrWhiteSpace()) + { + value = null; + } + } + + Type valueType; + //convert the string to a known type + switch (GetDatabaseType()) + { + case DataTypeDatabaseType.Ntext: + case DataTypeDatabaseType.Nvarchar: + valueType = typeof(string); + break; + case DataTypeDatabaseType.Integer: + //ensure these are nullable so we can return a null if required + //NOTE: This is allowing type of 'long' because I think json.net will deserialize a numerical value as long + // instead of int. Even though our db will not support this (will get truncated), we'll at least parse to this. + + valueType = typeof(long?); + break; + case DataTypeDatabaseType.Date: + //ensure these are nullable so we can return a null if required + valueType = typeof(DateTime?); + break; + default: + throw new ArgumentOutOfRangeException(); + } + return value.TryConvertTo(valueType); + } + + /// + /// A method to deserialize the string value that has been saved in the content editor + /// to an object to be stored in the database. + /// + /// + /// + /// The current value that has been persisted to the database for this editor. This value may be usesful for + /// how the value then get's deserialized again to be re-persisted. In most cases it will probably not be used. + /// + /// + /// + /// By default this will attempt to automatically convert the string value to the value type supplied by ValueType. + /// + /// If overridden then the object returned must match the type supplied in the ValueType, otherwise persisting the + /// value to the DB will fail when it tries to validate the value type. + /// + public virtual object ConvertEditorToDb(ContentPropertyData editorValue, object currentValue) + { + var result = TryConvertValueToCrlType(editorValue.Value); + if (result.Success == false) + { + LogHelper.Warn("The value " + editorValue.Value + " cannot be converted to the type " + GetDatabaseType()); + return null; + } + return result.Result; + } + + //TODO: Change the result to object so we can pass back JSON or json converted clr types if we want! + + /// + /// A method used to format the database value to a value that can be used by the editor + /// + /// + /// + /// + /// The object returned will automatically be serialized into json notation. For most property editors + /// the value returned is probably just a string but in some cases a json structure will be returned. + /// + public virtual object ConvertDbToEditor(object dbValue) + { + if (dbValue == null) return string.Empty; + + switch (GetDatabaseType()) + { + case DataTypeDatabaseType.Ntext: + case DataTypeDatabaseType.Nvarchar: + //if it is a string type, we will attempt to see if it is json stored data, if it is we'll try to convert + //to a real json object so we can pass the true json object directly to angular! + var asString = dbValue.ToString(); + if (asString.DetectIsJson()) + { + try + { + var json = JsonConvert.DeserializeObject(asString); + return json; + } + catch + { + //swallow this exception, we thought it was json but it really isn't so continue returning a string + } + } + return dbValue.ToString(); + case DataTypeDatabaseType.Integer: + //we can just ToString() any of these types + return dbValue.ToString(); + case DataTypeDatabaseType.Date: + var date = dbValue.TryConvertTo(); + if (date.Success == false || date.Result == null) + { + return string.Empty; + } + //Dates will be formatted as yyyy-MM-dd HH:mm:ss + return date.Result.Value.ToIsoString(); + default: + throw new ArgumentOutOfRangeException(); + } + } + + /// + /// Converts the property db value to an XML fragment + /// + /// + /// + /// + /// By default this will just return the value of ConvertDbToString but ensure that if the db value type is nvarchar or text + /// it is a CDATA fragment, otherwise it is just a text fragment. + /// + public virtual XNode ConvertDbToXml(Property property) + { + switch (GetDatabaseType()) + { + case DataTypeDatabaseType.Date: + case DataTypeDatabaseType.Integer: + return new XText(ConvertDbToString(property)); + case DataTypeDatabaseType.Nvarchar: + case DataTypeDatabaseType.Ntext: + //put text in cdata + return new XCData(ConvertDbToString(property)); + default: + throw new ArgumentOutOfRangeException(); + } + } + + /// + /// Converts the property value for use in the front-end cache + /// + /// + /// + public virtual string ConvertDbToString(Property property) + { + if (property.Value == null) + { + return string.Empty; + } + + switch (GetDatabaseType()) + { + case DataTypeDatabaseType.Nvarchar: + case DataTypeDatabaseType.Ntext: + property.Value.ToXmlString(); + return property.Value.ToXmlString(); + case DataTypeDatabaseType.Integer: + return property.Value.ToXmlString(property.Value.GetType()); + case DataTypeDatabaseType.Date: + //treat dates differently, output the format as xml format + if (property.Value == null) + { + return string.Empty; + } + var date = property.Value.TryConvertTo(); + if (date.Success == false || date.Result == null) + { + return string.Empty; + } + return date.Result.ToXmlString(); + default: + throw new ArgumentOutOfRangeException(); + } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueTypeAttribute.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueTypeAttribute.cs index 5d41b7f184..a2b03ff4bc 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyValueTypeAttribute.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyValueTypeAttribute.cs @@ -1,26 +1,26 @@ -using System; - -namespace Umbraco.Core.PropertyEditors -{ - /// - /// Indicates the CLR type of property object values returned by a converter. - /// - /// Use this attribute to mark property values converters. - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - public class PropertyValueTypeAttribute : Attribute - { - /// - /// Initializes a new instance of the class with a type. - /// - /// The type. - public PropertyValueTypeAttribute(Type type) - { - Type = type; - } - - /// - /// Gets or sets the type. - /// - public Type Type { get; private set; } - } -} +using System; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Indicates the CLR type of property object values returned by a converter. + /// + /// Use this attribute to mark property values converters. + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public class PropertyValueTypeAttribute : Attribute + { + /// + /// Initializes a new instance of the class with a type. + /// + /// The type. + public PropertyValueTypeAttribute(Type type) + { + Type = type; + } + + /// + /// Gets or sets the type. + /// + public Type Type { get; private set; } + } +} diff --git a/src/Umbraco.Core/PropertyEditors/SupportTagsAttribute.cs b/src/Umbraco.Core/PropertyEditors/SupportTagsAttribute.cs index 334341c2f6..60f32c086d 100644 --- a/src/Umbraco.Core/PropertyEditors/SupportTagsAttribute.cs +++ b/src/Umbraco.Core/PropertyEditors/SupportTagsAttribute.cs @@ -1,55 +1,55 @@ -using System; - -namespace Umbraco.Core.PropertyEditors -{ - /// - /// An interface that indicates that a property editor supports tags and will store it's published tags into the tag db table - /// - [AttributeUsage(AttributeTargets.Class)] - public class SupportTagsAttribute : Attribute - { - public Type TagPropertyDefinitionType { get; private set; } - - /// - /// Defines a tag property definition type to invoke at runtime to get the tags configuration for a property - /// - /// - public SupportTagsAttribute(Type tagPropertyDefinitionType) - : this() - { - if (tagPropertyDefinitionType == null) throw new ArgumentNullException("tagPropertyDefinitionType"); - TagPropertyDefinitionType = tagPropertyDefinitionType; - } - - /// - /// Normal constructor specifying the default tags configuration for a property - /// - public SupportTagsAttribute() - { - ValueType = TagValueType.FromDelimitedValue; - Delimiter = ","; - ReplaceTags = true; - TagGroup = "default"; - } - - /// - /// Defines how the tag values will be extracted, default is FromDelimitedValue - /// - public TagValueType ValueType { get; set; } - - /// - /// Defines a custom delimiter, the default is a comma - /// - public string Delimiter { get; set; } - - /// - /// Determines whether or not to replace the tags with the new value or append them (true to replace, false to append), default is true - /// - public bool ReplaceTags { get; set; } - - /// - /// The tag group to use when tagging, the default is 'default' - /// - public string TagGroup { get; set; } - } +using System; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// An interface that indicates that a property editor supports tags and will store it's published tags into the tag db table + /// + [AttributeUsage(AttributeTargets.Class)] + public class SupportTagsAttribute : Attribute + { + public Type TagPropertyDefinitionType { get; private set; } + + /// + /// Defines a tag property definition type to invoke at runtime to get the tags configuration for a property + /// + /// + public SupportTagsAttribute(Type tagPropertyDefinitionType) + : this() + { + if (tagPropertyDefinitionType == null) throw new ArgumentNullException("tagPropertyDefinitionType"); + TagPropertyDefinitionType = tagPropertyDefinitionType; + } + + /// + /// Normal constructor specifying the default tags configuration for a property + /// + public SupportTagsAttribute() + { + ValueType = TagValueType.FromDelimitedValue; + Delimiter = ","; + ReplaceTags = true; + TagGroup = "default"; + } + + /// + /// Defines how the tag values will be extracted, default is FromDelimitedValue + /// + public TagValueType ValueType { get; set; } + + /// + /// Defines a custom delimiter, the default is a comma + /// + public string Delimiter { get; set; } + + /// + /// Determines whether or not to replace the tags with the new value or append them (true to replace, false to append), default is true + /// + public bool ReplaceTags { get; set; } + + /// + /// The tag group to use when tagging, the default is 'default' + /// + public string TagGroup { get; set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/TagPropertyDefinition.cs b/src/Umbraco.Core/PropertyEditors/TagPropertyDefinition.cs index a4a8d6dd30..743cd08517 100644 --- a/src/Umbraco.Core/PropertyEditors/TagPropertyDefinition.cs +++ b/src/Umbraco.Core/PropertyEditors/TagPropertyDefinition.cs @@ -1,51 +1,51 @@ -using Umbraco.Core.Models.Editors; - -namespace Umbraco.Core.PropertyEditors -{ - /// - /// Allows for dynamically changing how a property's data is tagged at runtime during property setting - /// - public abstract class TagPropertyDefinition - { - /// - /// The property data that will create the tag data - /// - public ContentPropertyData PropertySaving { get; private set; } - - /// - /// The attribute that has specified this definition type - /// - public SupportTagsAttribute TagsAttribute { get; set; } - - /// - /// Constructor specifies the defaults and sets the ContentPropertyData being used to set the tag values which - /// can be used to dynamically adjust the tags definition for this property. - /// - /// - /// - protected TagPropertyDefinition(ContentPropertyData propertySaving, SupportTagsAttribute tagsAttribute) - { - PropertySaving = propertySaving; - TagsAttribute = tagsAttribute; - Delimiter = tagsAttribute.Delimiter; - ReplaceTags = tagsAttribute.ReplaceTags; - TagGroup = tagsAttribute.TagGroup; - } - - /// - /// Defines a custom delimiter, the default is a comma - /// - public virtual string Delimiter { get; private set; } - - /// - /// Determines whether or not to replace the tags with the new value or append them (true to replace, false to append), default is true - /// - public virtual bool ReplaceTags { get; private set; } - - /// - /// The tag group to use when tagging, the default is 'default' - /// - public virtual string TagGroup { get; private set; } - } - +using Umbraco.Core.Models.Editors; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Allows for dynamically changing how a property's data is tagged at runtime during property setting + /// + public abstract class TagPropertyDefinition + { + /// + /// The property data that will create the tag data + /// + public ContentPropertyData PropertySaving { get; private set; } + + /// + /// The attribute that has specified this definition type + /// + public SupportTagsAttribute TagsAttribute { get; set; } + + /// + /// Constructor specifies the defaults and sets the ContentPropertyData being used to set the tag values which + /// can be used to dynamically adjust the tags definition for this property. + /// + /// + /// + protected TagPropertyDefinition(ContentPropertyData propertySaving, SupportTagsAttribute tagsAttribute) + { + PropertySaving = propertySaving; + TagsAttribute = tagsAttribute; + Delimiter = tagsAttribute.Delimiter; + ReplaceTags = tagsAttribute.ReplaceTags; + TagGroup = tagsAttribute.TagGroup; + } + + /// + /// Defines a custom delimiter, the default is a comma + /// + public virtual string Delimiter { get; private set; } + + /// + /// Determines whether or not to replace the tags with the new value or append them (true to replace, false to append), default is true + /// + public virtual bool ReplaceTags { get; private set; } + + /// + /// The tag group to use when tagging, the default is 'default' + /// + public virtual string TagGroup { get; private set; } + } + } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/TagValueType.cs b/src/Umbraco.Core/PropertyEditors/TagValueType.cs index fb55b9f440..e5f65933ab 100644 --- a/src/Umbraco.Core/PropertyEditors/TagValueType.cs +++ b/src/Umbraco.Core/PropertyEditors/TagValueType.cs @@ -1,21 +1,21 @@ -namespace Umbraco.Core.PropertyEditors -{ - /// - /// Determines how the value for tags is extracted from the property - /// - public enum TagValueType - { - /// - /// The list of tags will be extracted from the string value by a delimiter - /// - FromDelimitedValue, - - /// - /// The list of tags will be supplied by the property editor's ConvertEditorToDb method result which will need to return an IEnumerable{string} value - /// - /// - /// if the ConvertEditorToDb doesn't return an IEnumerable{string} then an exception will be thrown. - /// - CustomTagList - } +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Determines how the value for tags is extracted from the property + /// + public enum TagValueType + { + /// + /// The list of tags will be extracted from the string value by a delimiter + /// + FromDelimitedValue, + + /// + /// The list of tags will be supplied by the property editor's ConvertEditorToDb method result which will need to return an IEnumerable{string} value + /// + /// + /// if the ConvertEditorToDb doesn't return an IEnumerable{string} then an exception will be thrown. + /// + CustomTagList + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/DatePickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/DatePickerValueConverter.cs index c74e1c3313..435a9833a6 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/DatePickerValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/DatePickerValueConverter.cs @@ -1,53 +1,53 @@ -using System; -using System.Globalization; -using System.Linq; -using System.Xml; -using Umbraco.Core.Models.PublishedContent; - -namespace Umbraco.Core.PropertyEditors.ValueConverters -{ - [PropertyValueType(typeof(DateTime))] - [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] - public class DatePickerValueConverter : PropertyValueConverterBase - { - private static readonly string[] PropertyEditorAliases = - { - Constants.PropertyEditors.DateTimeAlias, - Constants.PropertyEditors.DateAlias - }; - - public override bool IsConverter(PublishedPropertyType propertyType) - { - return PropertyEditorAliases.Contains(propertyType.PropertyEditorAlias); - } - - public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) - { - if (source == null) return DateTime.MinValue; - - // in XML a DateTime is: string - format "yyyy-MM-ddTHH:mm:ss" - var sourceString = source as string; - if (sourceString != null) - { - DateTime value; - return DateTime.TryParseExact(sourceString, "yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out value) - ? value - : DateTime.MinValue; - } - - // in the database a DateTime is: DateTime - // default value is: DateTime.MinValue - return (source is DateTime) - ? source - : DateTime.MinValue; - } - - // default ConvertSourceToObject just returns source ie a DateTime value - - public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview) - { - // source should come from ConvertSource and be a DateTime already - return XmlConvert.ToString((DateTime) source, "yyyy-MM-ddTHH:mm:ss"); - } - } +using System; +using System.Globalization; +using System.Linq; +using System.Xml; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + [PropertyValueType(typeof(DateTime))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class DatePickerValueConverter : PropertyValueConverterBase + { + private static readonly string[] PropertyEditorAliases = + { + Constants.PropertyEditors.DateTimeAlias, + Constants.PropertyEditors.DateAlias + }; + + public override bool IsConverter(PublishedPropertyType propertyType) + { + return PropertyEditorAliases.Contains(propertyType.PropertyEditorAlias); + } + + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + if (source == null) return DateTime.MinValue; + + // in XML a DateTime is: string - format "yyyy-MM-ddTHH:mm:ss" + var sourceString = source as string; + if (sourceString != null) + { + DateTime value; + return DateTime.TryParseExact(sourceString, "yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out value) + ? value + : DateTime.MinValue; + } + + // in the database a DateTime is: DateTime + // default value is: DateTime.MinValue + return (source is DateTime) + ? source + : DateTime.MinValue; + } + + // default ConvertSourceToObject just returns source ie a DateTime value + + public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview) + { + // source should come from ConvertSource and be a DateTime already + return XmlConvert.ToString((DateTime) source, "yyyy-MM-ddTHH:mm:ss"); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/IntegerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/IntegerValueConverter.cs index 96c908295b..c9b0306e32 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/IntegerValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/IntegerValueConverter.cs @@ -1,31 +1,31 @@ -using Umbraco.Core.Models.PublishedContent; - -namespace Umbraco.Core.PropertyEditors.ValueConverters -{ - [PropertyValueType(typeof(int))] - [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] - public class IntegerValueConverter : PropertyValueConverterBase - { - public override bool IsConverter(PublishedPropertyType propertyType) - { - return Constants.PropertyEditors.IntegerAlias.Equals(propertyType.PropertyEditorAlias); - } - - public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) - { - if (source == null) return 0; - - // in XML an integer is a string - var sourceString = source as string; - if (sourceString != null) - { - int i; - return (int.TryParse(sourceString, out i)) ? i : 0; - } - - // in the database an integer is an integer - // default value is zero - return (source is int) ? source : 0; - } - } -} +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + [PropertyValueType(typeof(int))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class IntegerValueConverter : PropertyValueConverterBase + { + public override bool IsConverter(PublishedPropertyType propertyType) + { + return Constants.PropertyEditors.IntegerAlias.Equals(propertyType.PropertyEditorAlias); + } + + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + if (source == null) return 0; + + // in XML an integer is a string + var sourceString = source as string; + if (sourceString != null) + { + int i; + return (int.TryParse(sourceString, out i)) ? i : 0; + } + + // in the database an integer is an integer + // default value is zero + return (source is int) ? source : 0; + } + } +} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs index bcecf4fd7a..1156f6aca3 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs @@ -1,49 +1,49 @@ -using System; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Umbraco.Core.Logging; -using Umbraco.Core.Models.PublishedContent; - -namespace Umbraco.Core.PropertyEditors.ValueConverters -{ - [PropertyValueType(typeof(JToken))] - [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] - public class JsonValueConverter : PropertyValueConverterBase - { - /// - /// It is a converter for any value type that is "JSON" - /// - /// - /// - public override bool IsConverter(PublishedPropertyType propertyType) - { - var propertyEditor = PropertyEditorResolver.Current.GetByAlias(propertyType.PropertyEditorAlias); - if (propertyEditor == null) return false; - return propertyEditor.ValueEditor.ValueType.InvariantEquals("json"); - } - - public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) - { - if (source == null) return null; - var sourceString = source.ToString(); - - if (sourceString.DetectIsJson()) - { - try - { - var obj = JsonConvert.DeserializeObject(sourceString); - return obj; - } - catch (Exception ex) - { - LogHelper.Error("Could not parse the string " + sourceString + " to a json object", ex); - } - } - - //it's not json, just return the string - return sourceString; - } - - //TODO: Now to convert that to XPath! - } +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Umbraco.Core.Logging; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + [PropertyValueType(typeof(JToken))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class JsonValueConverter : PropertyValueConverterBase + { + /// + /// It is a converter for any value type that is "JSON" + /// + /// + /// + public override bool IsConverter(PublishedPropertyType propertyType) + { + var propertyEditor = PropertyEditorResolver.Current.GetByAlias(propertyType.PropertyEditorAlias); + if (propertyEditor == null) return false; + return propertyEditor.ValueEditor.ValueType.InvariantEquals("json"); + } + + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + if (source == null) return null; + var sourceString = source.ToString(); + + if (sourceString.DetectIsJson()) + { + try + { + var obj = JsonConvert.DeserializeObject(sourceString); + return obj; + } + catch (Exception ex) + { + LogHelper.Error("Could not parse the string " + sourceString + " to a json object", ex); + } + } + + //it's not json, just return the string + return sourceString; + } + + //TODO: Now to convert that to XPath! + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs index ba870368e1..aae8da4be7 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs @@ -1,36 +1,36 @@ -using System; -using System.Web; -using Umbraco.Core.Models.PublishedContent; - -namespace Umbraco.Core.PropertyEditors.ValueConverters -{ - [PropertyValueType(typeof(IHtmlString))] - [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] - public class MarkdownEditorValueConverter : PropertyValueConverterBase - { - public override bool IsConverter(PublishedPropertyType propertyType) - { - return Constants.PropertyEditors.MarkdownEditorAlias.Equals(propertyType.PropertyEditorAlias); - } - - public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) - { - // in xml a string is: string - // in the database a string is: string - // default value is: null - return source; - } - - public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) - { - // source should come from ConvertSource and be a string (or null) already - return new HtmlString(source == null ? string.Empty : (string)source); - } - - public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview) - { - // source should come from ConvertSource and be a string (or null) already - return source; - } - } -} +using System; +using System.Web; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + [PropertyValueType(typeof(IHtmlString))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class MarkdownEditorValueConverter : PropertyValueConverterBase + { + public override bool IsConverter(PublishedPropertyType propertyType) + { + return Constants.PropertyEditors.MarkdownEditorAlias.Equals(propertyType.PropertyEditorAlias); + } + + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + // in xml a string is: string + // in the database a string is: string + // default value is: null + return source; + } + + public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) + { + // source should come from ConvertSource and be a string (or null) already + return new HtmlString(source == null ? string.Empty : (string)source); + } + + public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview) + { + // source should come from ConvertSource and be a string (or null) already + return source; + } + } +} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs index 022b7f194d..17ecbaeed1 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs @@ -1,64 +1,64 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml; -using System.Xml.Linq; -using Umbraco.Core.Models.PublishedContent; - -namespace Umbraco.Core.PropertyEditors.ValueConverters -{ - [PropertyValueType(typeof(IEnumerable))] - [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] - public class MultipleTextStringValueConverter : PropertyValueConverterBase - { - public override bool IsConverter(PublishedPropertyType propertyType) - { - return Constants.PropertyEditors.MultipleTextstringAlias.Equals(propertyType.PropertyEditorAlias); - } - - public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) - { - // data is (both in database and xml): - // - // - // Strong - // Flexible - // Efficient - // - // - - var sourceString = source.ToString(); - if (string.IsNullOrWhiteSpace(sourceString)) return Enumerable.Empty(); - - var values = new List(); - var pos = sourceString.IndexOf("", StringComparison.Ordinal); - while (pos >= 0) - { - pos += "".Length; - var npos = sourceString.IndexOf("<", pos, StringComparison.Ordinal); - var value = sourceString.Substring(pos, npos - pos); - values.Add(value); - pos = sourceString.IndexOf("", pos, StringComparison.Ordinal); - } - return values.ToArray(); - } - - public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview) - { - var d = new XmlDocument(); - var e = d.CreateElement("values"); - d.AppendChild(e); - - var values = (IEnumerable) source; - foreach (var value in values) - { - var ee = d.CreateElement("value"); - ee.InnerText = value; - e.AppendChild(ee); - } - - return d.CreateNavigator(); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml; +using System.Xml.Linq; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + [PropertyValueType(typeof(IEnumerable))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class MultipleTextStringValueConverter : PropertyValueConverterBase + { + public override bool IsConverter(PublishedPropertyType propertyType) + { + return Constants.PropertyEditors.MultipleTextstringAlias.Equals(propertyType.PropertyEditorAlias); + } + + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + // data is (both in database and xml): + // + // + // Strong + // Flexible + // Efficient + // + // + + var sourceString = source.ToString(); + if (string.IsNullOrWhiteSpace(sourceString)) return Enumerable.Empty(); + + var values = new List(); + var pos = sourceString.IndexOf("", StringComparison.Ordinal); + while (pos >= 0) + { + pos += "".Length; + var npos = sourceString.IndexOf("<", pos, StringComparison.Ordinal); + var value = sourceString.Substring(pos, npos - pos); + values.Add(value); + pos = sourceString.IndexOf("", pos, StringComparison.Ordinal); + } + return values.ToArray(); + } + + public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview) + { + var d = new XmlDocument(); + var e = d.CreateElement("values"); + d.AppendChild(e); + + var values = (IEnumerable) source; + foreach (var value in values) + { + var ee = d.CreateElement("value"); + ee.InnerText = value; + e.AppendChild(ee); + } + + return d.CreateNavigator(); + } + } +} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/TextStringValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/TextStringValueConverter.cs index d00476f4ba..c05ae4a0e9 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/TextStringValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/TextStringValueConverter.cs @@ -1,38 +1,38 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Umbraco.Core.Models.PublishedContent; - -namespace Umbraco.Core.PropertyEditors.ValueConverters -{ - [PropertyValueType(typeof(string))] - [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] - public class TextStringValueConverter : PropertyValueConverterBase - { - public override bool IsConverter(PublishedPropertyType propertyType) - { - return Constants.PropertyEditors.TextboxAlias.Equals(propertyType.PropertyEditorAlias); - } - - public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) - { - // in xml a string is: string - // in the database a string is: string - // default value is: null - return source; - } - - public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) - { - // source should come from ConvertSource and be a string (or null) already - return source ?? string.Empty; - } - - public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview) - { - // source should come from ConvertSource and be a string (or null) already - return source; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + [PropertyValueType(typeof(string))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class TextStringValueConverter : PropertyValueConverterBase + { + public override bool IsConverter(PublishedPropertyType propertyType) + { + return Constants.PropertyEditors.TextboxAlias.Equals(propertyType.PropertyEditorAlias); + } + + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + // in xml a string is: string + // in the database a string is: string + // default value is: null + return source; + } + + public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) + { + // source should come from ConvertSource and be a string (or null) already + return source ?? string.Empty; + } + + public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview) + { + // source should come from ConvertSource and be a string (or null) already + return source; + } + } +} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/TinyMceValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/TinyMceValueConverter.cs index 7b02a83176..4737fa4b91 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/TinyMceValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/TinyMceValueConverter.cs @@ -1,40 +1,40 @@ -using System; -using System.Web; -using Umbraco.Core.Models.PublishedContent; - -namespace Umbraco.Core.PropertyEditors.ValueConverters -{ - /// - /// Value converter for the RTE so that it always returns IHtmlString so that Html.Raw doesn't have to be used. - /// - // PropertyCacheLevel.Content is ok here because that version of RTE converter does not parse {locallink} nor executes macros - [PropertyValueType(typeof(IHtmlString))] - [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] - public class TinyMceValueConverter : PropertyValueConverterBase - { - public override bool IsConverter(PublishedPropertyType propertyType) - { - return propertyType.PropertyEditorAlias == Constants.PropertyEditors.TinyMCEv3Alias; - } - - public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) - { - // in xml a string is: string - // in the database a string is: string - // default value is: null - return source; - } - - public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) - { - // source should come from ConvertSource and be a string (or null) already - return new HtmlString(source == null ? string.Empty : (string)source); - } - - public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview) - { - // source should come from ConvertSource and be a string (or null) already - return source; - } - } +using System; +using System.Web; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + /// + /// Value converter for the RTE so that it always returns IHtmlString so that Html.Raw doesn't have to be used. + /// + // PropertyCacheLevel.Content is ok here because that version of RTE converter does not parse {locallink} nor executes macros + [PropertyValueType(typeof(IHtmlString))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class TinyMceValueConverter : PropertyValueConverterBase + { + public override bool IsConverter(PublishedPropertyType propertyType) + { + return propertyType.PropertyEditorAlias == Constants.PropertyEditors.TinyMCEv3Alias; + } + + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + // in xml a string is: string + // in the database a string is: string + // default value is: null + return source; + } + + public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) + { + // source should come from ConvertSource and be a string (or null) already + return new HtmlString(source == null ? string.Empty : (string)source); + } + + public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview) + { + // source should come from ConvertSource and be a string (or null) already + return source; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/YesNoValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/YesNoValueConverter.cs index 191e5ca9db..444906e3a1 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/YesNoValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/YesNoValueConverter.cs @@ -1,35 +1,35 @@ -using System; -using Umbraco.Core.Models.PublishedContent; - -namespace Umbraco.Core.PropertyEditors.ValueConverters -{ - [PropertyValueType(typeof(bool))] - [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] - public class YesNoValueConverter : PropertyValueConverterBase - { - public override bool IsConverter(PublishedPropertyType propertyType) - { - return propertyType.PropertyEditorAlias == Constants.PropertyEditors.TrueFalseAlias; - } - - public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) - { - // in xml a boolean is: string - // in the database a boolean is: string "1" or "0" or empty - // the converter does not need to handle anything else ("true"...) - - // default value is: false - var sourceString = source as string; - if (sourceString == null) return false; - return sourceString == "1"; - } - - // default ConvertSourceToObject just returns source ie a boolean value - - public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview) - { - // source should come from ConvertSource and be a boolean already - return (bool) source ? "1" : "0"; - } - } -} +using System; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Core.PropertyEditors.ValueConverters +{ + [PropertyValueType(typeof(bool))] + [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] + public class YesNoValueConverter : PropertyValueConverterBase + { + public override bool IsConverter(PublishedPropertyType propertyType) + { + return propertyType.PropertyEditorAlias == Constants.PropertyEditors.TrueFalseAlias; + } + + public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) + { + // in xml a boolean is: string + // in the database a boolean is: string "1" or "0" or empty + // the converter does not need to handle anything else ("true"...) + + // default value is: false + var sourceString = source as string; + if (sourceString == null) return false; + return sourceString == "1"; + } + + // default ConvertSourceToObject just returns source ie a boolean value + + public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview) + { + // source should come from ConvertSource and be a boolean already + return (bool) source ? "1" : "0"; + } + } +} diff --git a/src/Umbraco.Core/Security/MembershipProviderBase.cs b/src/Umbraco.Core/Security/MembershipProviderBase.cs index 10a05d042e..d1129e097d 100644 --- a/src/Umbraco.Core/Security/MembershipProviderBase.cs +++ b/src/Umbraco.Core/Security/MembershipProviderBase.cs @@ -1,605 +1,605 @@ -using System; -using System.Collections.Specialized; -using System.Configuration.Provider; -using System.Security.Cryptography; -using System.Text; -using System.Text.RegularExpressions; -using System.Web.Configuration; -using System.Web.Hosting; -using System.Web.Security; - -namespace Umbraco.Core.Security -{ - /// - /// A base membership provider class offering much of the underlying functionality for initializing and password encryption/hashing. - /// - public abstract class MembershipProviderBase : MembershipProvider - { - /// - /// Providers can override this setting, default is 7 - /// - protected virtual int DefaultMinPasswordLength - { - get { return 7; } - } - - /// - /// Providers can override this setting, default is 1 - /// - protected virtual int DefaultMinNonAlphanumericChars - { - get { return 1; } - } - - /// - /// Providers can override this setting, default is false to use better security - /// - protected virtual bool DefaultUseLegacyEncoding - { - get { return false; } - } - - /// - /// Providers can override this setting, by default this is false which means that the provider will - /// authenticate the username + password when ChangePassword is called. This property exists purely for - /// backwards compatibility. - /// - internal virtual bool AllowManuallyChangingPassword - { - get { return false; } - } - - private string _applicationName; - private bool _enablePasswordReset; - private bool _enablePasswordRetrieval; - private int _maxInvalidPasswordAttempts; - private int _minRequiredNonAlphanumericCharacters; - private int _minRequiredPasswordLength; - private int _passwordAttemptWindow; - private MembershipPasswordFormat _passwordFormat; - private string _passwordStrengthRegularExpression; - private bool _requiresQuestionAndAnswer; - private bool _requiresUniqueEmail; - private bool _useLegacyEncoding; - - #region Properties - - - /// - /// Indicates whether the membership provider is configured to allow users to reset their passwords. - /// - /// - /// true if the membership provider supports password reset; otherwise, false. The default is true. - public override bool EnablePasswordReset - { - get { return _enablePasswordReset; } - } - - /// - /// Indicates whether the membership provider is configured to allow users to retrieve their passwords. - /// - /// - /// true if the membership provider is configured to support password retrieval; otherwise, false. The default is false. - public override bool EnablePasswordRetrieval - { - get { return _enablePasswordRetrieval; } - } - - /// - /// Gets the number of invalid password or password-answer attempts allowed before the membership user is locked out. - /// - /// - /// The number of invalid password or password-answer attempts allowed before the membership user is locked out. - public override int MaxInvalidPasswordAttempts - { - get { return _maxInvalidPasswordAttempts; } - } - - /// - /// Gets the minimum number of special characters that must be present in a valid password. - /// - /// - /// The minimum number of special characters that must be present in a valid password. - public override int MinRequiredNonAlphanumericCharacters - { - get { return _minRequiredNonAlphanumericCharacters; } - } - - /// - /// Gets the minimum length required for a password. - /// - /// - /// The minimum length required for a password. - public override int MinRequiredPasswordLength - { - get { return _minRequiredPasswordLength; } - } - - /// - /// Gets the number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out. - /// - /// - /// The number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out. - public override int PasswordAttemptWindow - { - get { return _passwordAttemptWindow; } - } - - /// - /// Gets a value indicating the format for storing passwords in the membership data store. - /// - /// - /// One of the values indicating the format for storing passwords in the data store. - public override MembershipPasswordFormat PasswordFormat - { - get { return _passwordFormat; } - } - - /// - /// Gets the regular expression used to evaluate a password. - /// - /// - /// A regular expression used to evaluate a password. - public override string PasswordStrengthRegularExpression - { - get { return _passwordStrengthRegularExpression; } - } - - /// - /// Gets a value indicating whether the membership provider is configured to require the user to answer a password question for password reset and retrieval. - /// - /// - /// true if a password answer is required for password reset and retrieval; otherwise, false. The default is true. - public override bool RequiresQuestionAndAnswer - { - get { return _requiresQuestionAndAnswer; } - } - - /// - /// Gets a value indicating whether the membership provider is configured to require a unique e-mail address for each user name. - /// - /// - /// true if the membership provider requires a unique e-mail address; otherwise, false. The default is true. - public override bool RequiresUniqueEmail - { - get { return _requiresUniqueEmail; } - } - - /// - /// The name of the application using the custom membership provider. - /// - /// - /// The name of the application using the custom membership provider. - public override string ApplicationName - { - get - { - return _applicationName; - } - set - { - if (string.IsNullOrEmpty(value)) - throw new ProviderException("ApplicationName cannot be empty."); - - if (value.Length > 0x100) - throw new ProviderException("Provider application name too long."); - - _applicationName = value; - } - } - - #endregion - - /// - /// Initializes the provider. - /// - /// The friendly name of the provider. - /// A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider. - /// The name of the provider is null. - /// An attempt is made to call - /// on a provider after the provider - /// has already been initialized. - /// The name of the provider has a length of zero. - public override void Initialize(string name, NameValueCollection config) - { - // Initialize base provider class - base.Initialize(name, config); - - _enablePasswordRetrieval = config.GetValue("enablePasswordRetrieval", false); - _enablePasswordReset = config.GetValue("enablePasswordReset", false); - _requiresQuestionAndAnswer = config.GetValue("requiresQuestionAndAnswer", false); - _requiresUniqueEmail = config.GetValue("requiresUniqueEmail", false); - _maxInvalidPasswordAttempts = GetIntValue(config, "maxInvalidPasswordAttempts", 5, false, 0); - _passwordAttemptWindow = GetIntValue(config, "passwordAttemptWindow", 10, false, 0); - _minRequiredPasswordLength = GetIntValue(config, "minRequiredPasswordLength", DefaultMinPasswordLength, true, 0x80); - _minRequiredNonAlphanumericCharacters = GetIntValue(config, "minRequiredNonalphanumericCharacters", DefaultMinNonAlphanumericChars, true, 0x80); - _passwordStrengthRegularExpression = config["passwordStrengthRegularExpression"]; - - _applicationName = config["applicationName"]; - if (string.IsNullOrEmpty(_applicationName)) - _applicationName = GetDefaultAppName(); - - //by default we will continue using the legacy encoding. - _useLegacyEncoding = config.GetValue("useLegacyEncoding", DefaultUseLegacyEncoding); - - // make sure password format is clear by default. - string str = config["passwordFormat"] ?? "Clear"; - - switch (str.ToLower()) - { - case "clear": - _passwordFormat = MembershipPasswordFormat.Clear; - break; - - case "encrypted": - _passwordFormat = MembershipPasswordFormat.Encrypted; - break; - - case "hashed": - _passwordFormat = MembershipPasswordFormat.Hashed; - break; - - default: - throw new ProviderException("Provider bad password format"); - } - - if ((PasswordFormat == MembershipPasswordFormat.Hashed) && EnablePasswordRetrieval) - throw new ProviderException("Provider can not retrieve hashed password"); - - } - - /// - /// Override this method to ensure the password is valid before raising the event - /// - /// - protected override void OnValidatingPassword(ValidatePasswordEventArgs e) - { - var attempt = IsPasswordValid(e.Password, MinRequiredNonAlphanumericCharacters, PasswordStrengthRegularExpression, MinRequiredPasswordLength); - if (attempt.Success == false) - { - e.Cancel = true; - return; - } - - base.OnValidatingPassword(e); - } - - protected internal enum PasswordValidityError - { - Ok, - Length, - AlphanumericChars, - Strength - } - - /// - /// Checks to ensure the AllowManuallyChangingPassword rule is adhered to - /// - /// - /// - /// - /// - public sealed override bool ChangePassword(string username, string oldPassword, string newPassword) - { - if (oldPassword.IsNullOrWhiteSpace() && AllowManuallyChangingPassword == false) - { - //If the old password is empty and AllowManuallyChangingPassword is false, than this provider cannot just arbitrarily change the password - throw new NotSupportedException("This provider does not support manually changing the password"); - } - - return PerformChangePassword(username, oldPassword, newPassword); - } - - protected abstract bool PerformChangePassword(string username, string oldPassword, string newPassword); - - protected internal static Attempt IsPasswordValid(string password, int minRequiredNonAlphanumericChars, string strengthRegex, int minLength) - { - if (minRequiredNonAlphanumericChars > 0) - { - var nonAlphaNumeric = Regex.Replace(password, "[a-zA-Z0-9]", "", RegexOptions.Multiline | RegexOptions.IgnoreCase); - if (nonAlphaNumeric.Length < minRequiredNonAlphanumericChars) - { - return Attempt.Fail(PasswordValidityError.AlphanumericChars); - } - } - - if (string.IsNullOrEmpty(strengthRegex) == false) - { - if (Regex.IsMatch(password, strengthRegex, RegexOptions.Compiled) == false) - { - return Attempt.Fail(PasswordValidityError.Strength); - } - - } - - if (password.Length < minLength) - { - return Attempt.Fail(PasswordValidityError.Length); - } - - return Attempt.Succeed(PasswordValidityError.Ok); - } - - /// - /// Gets the name of the default app. - /// - /// - internal static string GetDefaultAppName() - { - try - { - string applicationVirtualPath = HostingEnvironment.ApplicationVirtualPath; - if (string.IsNullOrEmpty(applicationVirtualPath)) - { - return "/"; - } - return applicationVirtualPath; - } - catch - { - return "/"; - } - } - - internal static int GetIntValue(NameValueCollection config, string valueName, int defaultValue, bool zeroAllowed, int maxValueAllowed) - { - int num; - string s = config[valueName]; - if (s == null) - { - return defaultValue; - } - if (!int.TryParse(s, out num)) - { - if (zeroAllowed) - { - throw new ProviderException("Value must be non negative integer"); - } - throw new ProviderException("Value must be positive integer"); - } - if (zeroAllowed && (num < 0)) - { - throw new ProviderException("Value must be non negativeinteger"); - } - if (!zeroAllowed && (num <= 0)) - { - throw new ProviderException("Value must be positive integer"); - } - if ((maxValueAllowed > 0) && (num > maxValueAllowed)) - { - throw new ProviderException("Value too big"); - } - return num; - } - - protected string FormatPasswordForStorage(string pass, string salt) - { - if (_useLegacyEncoding) - { - return pass; - } - - //the better way, we use salt per member - return salt + pass; - } - - protected string EncryptOrHashPassword(string pass, string salt) - { - //if we are doing it the old way - - if (_useLegacyEncoding) - { - return LegacyEncodePassword(pass); - } - - //This is the correct way to implement this (as per the sql membership provider) - - if ((int)PasswordFormat == 0) - return pass; - var bytes = Encoding.Unicode.GetBytes(pass); - var numArray1 = Convert.FromBase64String(salt); - byte[] inArray; - if ((int)PasswordFormat == 1) - { - var hashAlgorithm = GetHashAlgorithm(pass); - var algorithm = hashAlgorithm as KeyedHashAlgorithm; - if (algorithm != null) - { - var keyedHashAlgorithm = algorithm; - if (keyedHashAlgorithm.Key.Length == numArray1.Length) - keyedHashAlgorithm.Key = numArray1; - else if (keyedHashAlgorithm.Key.Length < numArray1.Length) - { - var numArray2 = new byte[keyedHashAlgorithm.Key.Length]; - Buffer.BlockCopy(numArray1, 0, numArray2, 0, numArray2.Length); - keyedHashAlgorithm.Key = numArray2; - } - else - { - var numArray2 = new byte[keyedHashAlgorithm.Key.Length]; - var dstOffset = 0; - while (dstOffset < numArray2.Length) - { - var count = Math.Min(numArray1.Length, numArray2.Length - dstOffset); - Buffer.BlockCopy(numArray1, 0, numArray2, dstOffset, count); - dstOffset += count; - } - keyedHashAlgorithm.Key = numArray2; - } - inArray = keyedHashAlgorithm.ComputeHash(bytes); - } - else - { - var buffer = new byte[numArray1.Length + bytes.Length]; - Buffer.BlockCopy(numArray1, 0, buffer, 0, numArray1.Length); - Buffer.BlockCopy(bytes, 0, buffer, numArray1.Length, bytes.Length); - inArray = hashAlgorithm.ComputeHash(buffer); - } - } - else - { - var password = new byte[numArray1.Length + bytes.Length]; - Buffer.BlockCopy(numArray1, 0, password, 0, numArray1.Length); - Buffer.BlockCopy(bytes, 0, password, numArray1.Length, bytes.Length); - inArray = EncryptPassword(password, MembershipPasswordCompatibilityMode.Framework40); - } - return Convert.ToBase64String(inArray); - } - - /// - /// Encrypt/hash a new password with a new salt - /// - /// - /// - /// - protected string EncryptOrHashNewPassword(string newPassword, out string salt) - { - salt = GenerateSalt(); - return EncryptOrHashPassword(newPassword, salt); - } - - /// - /// Gets the encrypted or hashed string of an existing password for an existing user - /// - /// The stored string for the password - /// - protected string EncryptOrHashExistingPassword(string storedPassword) - { - if (_useLegacyEncoding) - { - return EncryptOrHashPassword(storedPassword, storedPassword); - } - else - { - string salt; - var pass = StoredPassword(storedPassword, PasswordFormat, out salt); - return EncryptOrHashPassword(pass, salt); - } - } - - protected string DecodePassword(string pass) - { - //if we are doing it the old way - - if (_useLegacyEncoding) - { - return LegacyUnEncodePassword(pass); - } - - //This is the correct way to implement this (as per the sql membership provider) - - switch ((int)PasswordFormat) - { - case 0: - return pass; - case 1: - throw new ProviderException("Provider can not decode hashed password"); - default: - var bytes = DecryptPassword(Convert.FromBase64String(pass)); - return bytes == null ? null : Encoding.Unicode.GetString(bytes, 16, bytes.Length - 16); - } - } - - /// - /// Returns the hashed password without the salt if it is hashed - /// - /// - /// - /// returns the salt - /// - internal static string StoredPassword(string storedString, MembershipPasswordFormat format, out string salt) - { - switch (format) - { - case MembershipPasswordFormat.Hashed: - var saltLen = GenerateSalt(); - salt = storedString.Substring(0, saltLen.Length); - return storedString.Substring(saltLen.Length); - case MembershipPasswordFormat.Clear: - case MembershipPasswordFormat.Encrypted: - default: - salt = string.Empty; - return storedString; - - } - } - - protected internal static string GenerateSalt() - { - var numArray = new byte[16]; - new RNGCryptoServiceProvider().GetBytes(numArray); - return Convert.ToBase64String(numArray); - } - - protected HashAlgorithm GetHashAlgorithm(string password) - { - if (_useLegacyEncoding) - { - //before we were never checking for an algorithm type so we were always using HMACSHA1 - // for any SHA specified algorithm :( so we'll need to keep doing that for backwards compat support. - if (Membership.HashAlgorithmType.InvariantContains("SHA")) - { - return new HMACSHA1 - { - //the legacy salt was actually the password :( - Key = Encoding.Unicode.GetBytes(password) - }; - } - } - - //get the algorithm by name - - return HashAlgorithm.Create(Membership.HashAlgorithmType); - } - - /// - /// Encodes the password. - /// - /// The password. - /// The encoded password. - protected string LegacyEncodePassword(string password) - { - string encodedPassword = password; - switch (PasswordFormat) - { - case MembershipPasswordFormat.Clear: - break; - case MembershipPasswordFormat.Encrypted: - encodedPassword = - Convert.ToBase64String(EncryptPassword(Encoding.Unicode.GetBytes(password))); - break; - case MembershipPasswordFormat.Hashed: - var hashAlgorith = GetHashAlgorithm(password); - encodedPassword = Convert.ToBase64String(hashAlgorith.ComputeHash(Encoding.Unicode.GetBytes(password))); - break; - default: - throw new ProviderException("Unsupported password format."); - } - return encodedPassword; - } - - /// - /// Unencode password. - /// - /// The encoded password. - /// The unencoded password. - protected string LegacyUnEncodePassword(string encodedPassword) - { - string password = encodedPassword; - switch (PasswordFormat) - { - case MembershipPasswordFormat.Clear: - break; - case MembershipPasswordFormat.Encrypted: - password = Encoding.Unicode.GetString(DecryptPassword(Convert.FromBase64String(password))); - break; - case MembershipPasswordFormat.Hashed: - throw new ProviderException("Cannot unencode a hashed password."); - default: - throw new ProviderException("Unsupported password format."); - } - return password; - } - - } +using System; +using System.Collections.Specialized; +using System.Configuration.Provider; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using System.Web.Configuration; +using System.Web.Hosting; +using System.Web.Security; + +namespace Umbraco.Core.Security +{ + /// + /// A base membership provider class offering much of the underlying functionality for initializing and password encryption/hashing. + /// + public abstract class MembershipProviderBase : MembershipProvider + { + /// + /// Providers can override this setting, default is 7 + /// + protected virtual int DefaultMinPasswordLength + { + get { return 7; } + } + + /// + /// Providers can override this setting, default is 1 + /// + protected virtual int DefaultMinNonAlphanumericChars + { + get { return 1; } + } + + /// + /// Providers can override this setting, default is false to use better security + /// + protected virtual bool DefaultUseLegacyEncoding + { + get { return false; } + } + + /// + /// Providers can override this setting, by default this is false which means that the provider will + /// authenticate the username + password when ChangePassword is called. This property exists purely for + /// backwards compatibility. + /// + internal virtual bool AllowManuallyChangingPassword + { + get { return false; } + } + + private string _applicationName; + private bool _enablePasswordReset; + private bool _enablePasswordRetrieval; + private int _maxInvalidPasswordAttempts; + private int _minRequiredNonAlphanumericCharacters; + private int _minRequiredPasswordLength; + private int _passwordAttemptWindow; + private MembershipPasswordFormat _passwordFormat; + private string _passwordStrengthRegularExpression; + private bool _requiresQuestionAndAnswer; + private bool _requiresUniqueEmail; + private bool _useLegacyEncoding; + + #region Properties + + + /// + /// Indicates whether the membership provider is configured to allow users to reset their passwords. + /// + /// + /// true if the membership provider supports password reset; otherwise, false. The default is true. + public override bool EnablePasswordReset + { + get { return _enablePasswordReset; } + } + + /// + /// Indicates whether the membership provider is configured to allow users to retrieve their passwords. + /// + /// + /// true if the membership provider is configured to support password retrieval; otherwise, false. The default is false. + public override bool EnablePasswordRetrieval + { + get { return _enablePasswordRetrieval; } + } + + /// + /// Gets the number of invalid password or password-answer attempts allowed before the membership user is locked out. + /// + /// + /// The number of invalid password or password-answer attempts allowed before the membership user is locked out. + public override int MaxInvalidPasswordAttempts + { + get { return _maxInvalidPasswordAttempts; } + } + + /// + /// Gets the minimum number of special characters that must be present in a valid password. + /// + /// + /// The minimum number of special characters that must be present in a valid password. + public override int MinRequiredNonAlphanumericCharacters + { + get { return _minRequiredNonAlphanumericCharacters; } + } + + /// + /// Gets the minimum length required for a password. + /// + /// + /// The minimum length required for a password. + public override int MinRequiredPasswordLength + { + get { return _minRequiredPasswordLength; } + } + + /// + /// Gets the number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out. + /// + /// + /// The number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out. + public override int PasswordAttemptWindow + { + get { return _passwordAttemptWindow; } + } + + /// + /// Gets a value indicating the format for storing passwords in the membership data store. + /// + /// + /// One of the values indicating the format for storing passwords in the data store. + public override MembershipPasswordFormat PasswordFormat + { + get { return _passwordFormat; } + } + + /// + /// Gets the regular expression used to evaluate a password. + /// + /// + /// A regular expression used to evaluate a password. + public override string PasswordStrengthRegularExpression + { + get { return _passwordStrengthRegularExpression; } + } + + /// + /// Gets a value indicating whether the membership provider is configured to require the user to answer a password question for password reset and retrieval. + /// + /// + /// true if a password answer is required for password reset and retrieval; otherwise, false. The default is true. + public override bool RequiresQuestionAndAnswer + { + get { return _requiresQuestionAndAnswer; } + } + + /// + /// Gets a value indicating whether the membership provider is configured to require a unique e-mail address for each user name. + /// + /// + /// true if the membership provider requires a unique e-mail address; otherwise, false. The default is true. + public override bool RequiresUniqueEmail + { + get { return _requiresUniqueEmail; } + } + + /// + /// The name of the application using the custom membership provider. + /// + /// + /// The name of the application using the custom membership provider. + public override string ApplicationName + { + get + { + return _applicationName; + } + set + { + if (string.IsNullOrEmpty(value)) + throw new ProviderException("ApplicationName cannot be empty."); + + if (value.Length > 0x100) + throw new ProviderException("Provider application name too long."); + + _applicationName = value; + } + } + + #endregion + + /// + /// Initializes the provider. + /// + /// The friendly name of the provider. + /// A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider. + /// The name of the provider is null. + /// An attempt is made to call + /// on a provider after the provider + /// has already been initialized. + /// The name of the provider has a length of zero. + public override void Initialize(string name, NameValueCollection config) + { + // Initialize base provider class + base.Initialize(name, config); + + _enablePasswordRetrieval = config.GetValue("enablePasswordRetrieval", false); + _enablePasswordReset = config.GetValue("enablePasswordReset", false); + _requiresQuestionAndAnswer = config.GetValue("requiresQuestionAndAnswer", false); + _requiresUniqueEmail = config.GetValue("requiresUniqueEmail", false); + _maxInvalidPasswordAttempts = GetIntValue(config, "maxInvalidPasswordAttempts", 5, false, 0); + _passwordAttemptWindow = GetIntValue(config, "passwordAttemptWindow", 10, false, 0); + _minRequiredPasswordLength = GetIntValue(config, "minRequiredPasswordLength", DefaultMinPasswordLength, true, 0x80); + _minRequiredNonAlphanumericCharacters = GetIntValue(config, "minRequiredNonalphanumericCharacters", DefaultMinNonAlphanumericChars, true, 0x80); + _passwordStrengthRegularExpression = config["passwordStrengthRegularExpression"]; + + _applicationName = config["applicationName"]; + if (string.IsNullOrEmpty(_applicationName)) + _applicationName = GetDefaultAppName(); + + //by default we will continue using the legacy encoding. + _useLegacyEncoding = config.GetValue("useLegacyEncoding", DefaultUseLegacyEncoding); + + // make sure password format is clear by default. + string str = config["passwordFormat"] ?? "Clear"; + + switch (str.ToLower()) + { + case "clear": + _passwordFormat = MembershipPasswordFormat.Clear; + break; + + case "encrypted": + _passwordFormat = MembershipPasswordFormat.Encrypted; + break; + + case "hashed": + _passwordFormat = MembershipPasswordFormat.Hashed; + break; + + default: + throw new ProviderException("Provider bad password format"); + } + + if ((PasswordFormat == MembershipPasswordFormat.Hashed) && EnablePasswordRetrieval) + throw new ProviderException("Provider can not retrieve hashed password"); + + } + + /// + /// Override this method to ensure the password is valid before raising the event + /// + /// + protected override void OnValidatingPassword(ValidatePasswordEventArgs e) + { + var attempt = IsPasswordValid(e.Password, MinRequiredNonAlphanumericCharacters, PasswordStrengthRegularExpression, MinRequiredPasswordLength); + if (attempt.Success == false) + { + e.Cancel = true; + return; + } + + base.OnValidatingPassword(e); + } + + protected internal enum PasswordValidityError + { + Ok, + Length, + AlphanumericChars, + Strength + } + + /// + /// Checks to ensure the AllowManuallyChangingPassword rule is adhered to + /// + /// + /// + /// + /// + public sealed override bool ChangePassword(string username, string oldPassword, string newPassword) + { + if (oldPassword.IsNullOrWhiteSpace() && AllowManuallyChangingPassword == false) + { + //If the old password is empty and AllowManuallyChangingPassword is false, than this provider cannot just arbitrarily change the password + throw new NotSupportedException("This provider does not support manually changing the password"); + } + + return PerformChangePassword(username, oldPassword, newPassword); + } + + protected abstract bool PerformChangePassword(string username, string oldPassword, string newPassword); + + protected internal static Attempt IsPasswordValid(string password, int minRequiredNonAlphanumericChars, string strengthRegex, int minLength) + { + if (minRequiredNonAlphanumericChars > 0) + { + var nonAlphaNumeric = Regex.Replace(password, "[a-zA-Z0-9]", "", RegexOptions.Multiline | RegexOptions.IgnoreCase); + if (nonAlphaNumeric.Length < minRequiredNonAlphanumericChars) + { + return Attempt.Fail(PasswordValidityError.AlphanumericChars); + } + } + + if (string.IsNullOrEmpty(strengthRegex) == false) + { + if (Regex.IsMatch(password, strengthRegex, RegexOptions.Compiled) == false) + { + return Attempt.Fail(PasswordValidityError.Strength); + } + + } + + if (password.Length < minLength) + { + return Attempt.Fail(PasswordValidityError.Length); + } + + return Attempt.Succeed(PasswordValidityError.Ok); + } + + /// + /// Gets the name of the default app. + /// + /// + internal static string GetDefaultAppName() + { + try + { + string applicationVirtualPath = HostingEnvironment.ApplicationVirtualPath; + if (string.IsNullOrEmpty(applicationVirtualPath)) + { + return "/"; + } + return applicationVirtualPath; + } + catch + { + return "/"; + } + } + + internal static int GetIntValue(NameValueCollection config, string valueName, int defaultValue, bool zeroAllowed, int maxValueAllowed) + { + int num; + string s = config[valueName]; + if (s == null) + { + return defaultValue; + } + if (!int.TryParse(s, out num)) + { + if (zeroAllowed) + { + throw new ProviderException("Value must be non negative integer"); + } + throw new ProviderException("Value must be positive integer"); + } + if (zeroAllowed && (num < 0)) + { + throw new ProviderException("Value must be non negativeinteger"); + } + if (!zeroAllowed && (num <= 0)) + { + throw new ProviderException("Value must be positive integer"); + } + if ((maxValueAllowed > 0) && (num > maxValueAllowed)) + { + throw new ProviderException("Value too big"); + } + return num; + } + + protected string FormatPasswordForStorage(string pass, string salt) + { + if (_useLegacyEncoding) + { + return pass; + } + + //the better way, we use salt per member + return salt + pass; + } + + protected string EncryptOrHashPassword(string pass, string salt) + { + //if we are doing it the old way + + if (_useLegacyEncoding) + { + return LegacyEncodePassword(pass); + } + + //This is the correct way to implement this (as per the sql membership provider) + + if ((int)PasswordFormat == 0) + return pass; + var bytes = Encoding.Unicode.GetBytes(pass); + var numArray1 = Convert.FromBase64String(salt); + byte[] inArray; + if ((int)PasswordFormat == 1) + { + var hashAlgorithm = GetHashAlgorithm(pass); + var algorithm = hashAlgorithm as KeyedHashAlgorithm; + if (algorithm != null) + { + var keyedHashAlgorithm = algorithm; + if (keyedHashAlgorithm.Key.Length == numArray1.Length) + keyedHashAlgorithm.Key = numArray1; + else if (keyedHashAlgorithm.Key.Length < numArray1.Length) + { + var numArray2 = new byte[keyedHashAlgorithm.Key.Length]; + Buffer.BlockCopy(numArray1, 0, numArray2, 0, numArray2.Length); + keyedHashAlgorithm.Key = numArray2; + } + else + { + var numArray2 = new byte[keyedHashAlgorithm.Key.Length]; + var dstOffset = 0; + while (dstOffset < numArray2.Length) + { + var count = Math.Min(numArray1.Length, numArray2.Length - dstOffset); + Buffer.BlockCopy(numArray1, 0, numArray2, dstOffset, count); + dstOffset += count; + } + keyedHashAlgorithm.Key = numArray2; + } + inArray = keyedHashAlgorithm.ComputeHash(bytes); + } + else + { + var buffer = new byte[numArray1.Length + bytes.Length]; + Buffer.BlockCopy(numArray1, 0, buffer, 0, numArray1.Length); + Buffer.BlockCopy(bytes, 0, buffer, numArray1.Length, bytes.Length); + inArray = hashAlgorithm.ComputeHash(buffer); + } + } + else + { + var password = new byte[numArray1.Length + bytes.Length]; + Buffer.BlockCopy(numArray1, 0, password, 0, numArray1.Length); + Buffer.BlockCopy(bytes, 0, password, numArray1.Length, bytes.Length); + inArray = EncryptPassword(password, MembershipPasswordCompatibilityMode.Framework40); + } + return Convert.ToBase64String(inArray); + } + + /// + /// Encrypt/hash a new password with a new salt + /// + /// + /// + /// + protected string EncryptOrHashNewPassword(string newPassword, out string salt) + { + salt = GenerateSalt(); + return EncryptOrHashPassword(newPassword, salt); + } + + /// + /// Gets the encrypted or hashed string of an existing password for an existing user + /// + /// The stored string for the password + /// + protected string EncryptOrHashExistingPassword(string storedPassword) + { + if (_useLegacyEncoding) + { + return EncryptOrHashPassword(storedPassword, storedPassword); + } + else + { + string salt; + var pass = StoredPassword(storedPassword, PasswordFormat, out salt); + return EncryptOrHashPassword(pass, salt); + } + } + + protected string DecodePassword(string pass) + { + //if we are doing it the old way + + if (_useLegacyEncoding) + { + return LegacyUnEncodePassword(pass); + } + + //This is the correct way to implement this (as per the sql membership provider) + + switch ((int)PasswordFormat) + { + case 0: + return pass; + case 1: + throw new ProviderException("Provider can not decode hashed password"); + default: + var bytes = DecryptPassword(Convert.FromBase64String(pass)); + return bytes == null ? null : Encoding.Unicode.GetString(bytes, 16, bytes.Length - 16); + } + } + + /// + /// Returns the hashed password without the salt if it is hashed + /// + /// + /// + /// returns the salt + /// + internal static string StoredPassword(string storedString, MembershipPasswordFormat format, out string salt) + { + switch (format) + { + case MembershipPasswordFormat.Hashed: + var saltLen = GenerateSalt(); + salt = storedString.Substring(0, saltLen.Length); + return storedString.Substring(saltLen.Length); + case MembershipPasswordFormat.Clear: + case MembershipPasswordFormat.Encrypted: + default: + salt = string.Empty; + return storedString; + + } + } + + protected internal static string GenerateSalt() + { + var numArray = new byte[16]; + new RNGCryptoServiceProvider().GetBytes(numArray); + return Convert.ToBase64String(numArray); + } + + protected HashAlgorithm GetHashAlgorithm(string password) + { + if (_useLegacyEncoding) + { + //before we were never checking for an algorithm type so we were always using HMACSHA1 + // for any SHA specified algorithm :( so we'll need to keep doing that for backwards compat support. + if (Membership.HashAlgorithmType.InvariantContains("SHA")) + { + return new HMACSHA1 + { + //the legacy salt was actually the password :( + Key = Encoding.Unicode.GetBytes(password) + }; + } + } + + //get the algorithm by name + + return HashAlgorithm.Create(Membership.HashAlgorithmType); + } + + /// + /// Encodes the password. + /// + /// The password. + /// The encoded password. + protected string LegacyEncodePassword(string password) + { + string encodedPassword = password; + switch (PasswordFormat) + { + case MembershipPasswordFormat.Clear: + break; + case MembershipPasswordFormat.Encrypted: + encodedPassword = + Convert.ToBase64String(EncryptPassword(Encoding.Unicode.GetBytes(password))); + break; + case MembershipPasswordFormat.Hashed: + var hashAlgorith = GetHashAlgorithm(password); + encodedPassword = Convert.ToBase64String(hashAlgorith.ComputeHash(Encoding.Unicode.GetBytes(password))); + break; + default: + throw new ProviderException("Unsupported password format."); + } + return encodedPassword; + } + + /// + /// Unencode password. + /// + /// The encoded password. + /// The unencoded password. + protected string LegacyUnEncodePassword(string encodedPassword) + { + string password = encodedPassword; + switch (PasswordFormat) + { + case MembershipPasswordFormat.Clear: + break; + case MembershipPasswordFormat.Encrypted: + password = Encoding.Unicode.GetString(DecryptPassword(Convert.FromBase64String(password))); + break; + case MembershipPasswordFormat.Hashed: + throw new ProviderException("Cannot unencode a hashed password."); + default: + throw new ProviderException("Unsupported password format."); + } + return password; + } + + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IApplicationTreeService.cs b/src/Umbraco.Core/Services/IApplicationTreeService.cs index bb7bb0f790..c7aecec6fb 100644 --- a/src/Umbraco.Core/Services/IApplicationTreeService.cs +++ b/src/Umbraco.Core/Services/IApplicationTreeService.cs @@ -1,61 +1,61 @@ -using System.Collections.Generic; -using Umbraco.Core.Models; - -namespace Umbraco.Core.Services -{ - public interface IApplicationTreeService - { - void Intitialize(IEnumerable existingTrees); - - /// - /// Creates a new application tree. - /// - /// if set to true [initialize]. - /// The sort order. - /// The application alias. - /// The alias. - /// The title. - /// The icon closed. - /// The icon opened. - /// The type. - void MakeNew(bool initialize, byte sortOrder, string applicationAlias, string alias, string title, string iconClosed, string iconOpened, string type); - - /// - /// Saves this instance. - /// - void SaveTree(ApplicationTree tree); - - /// - /// Deletes this instance. - /// - void DeleteTree(ApplicationTree tree); - - /// - /// Gets an ApplicationTree by it's tree alias. - /// - /// The tree alias. - /// An ApplicationTree instance - ApplicationTree GetByAlias(string treeAlias); - - /// - /// Gets all applicationTrees registered in umbraco from the umbracoAppTree table.. - /// - /// Returns a ApplicationTree Array - IEnumerable GetAll(); - - /// - /// Gets the application tree for the applcation with the specified alias - /// - /// The application alias. - /// Returns a ApplicationTree Array - IEnumerable GetApplicationTrees(string applicationAlias); - - /// - /// Gets the application tree for the applcation with the specified alias - /// - /// The application alias. - /// - /// Returns a ApplicationTree Array - IEnumerable GetApplicationTrees(string applicationAlias, bool onlyInitialized); - } +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Services +{ + public interface IApplicationTreeService + { + void Intitialize(IEnumerable existingTrees); + + /// + /// Creates a new application tree. + /// + /// if set to true [initialize]. + /// The sort order. + /// The application alias. + /// The alias. + /// The title. + /// The icon closed. + /// The icon opened. + /// The type. + void MakeNew(bool initialize, byte sortOrder, string applicationAlias, string alias, string title, string iconClosed, string iconOpened, string type); + + /// + /// Saves this instance. + /// + void SaveTree(ApplicationTree tree); + + /// + /// Deletes this instance. + /// + void DeleteTree(ApplicationTree tree); + + /// + /// Gets an ApplicationTree by it's tree alias. + /// + /// The tree alias. + /// An ApplicationTree instance + ApplicationTree GetByAlias(string treeAlias); + + /// + /// Gets all applicationTrees registered in umbraco from the umbracoAppTree table.. + /// + /// Returns a ApplicationTree Array + IEnumerable GetAll(); + + /// + /// Gets the application tree for the applcation with the specified alias + /// + /// The application alias. + /// Returns a ApplicationTree Array + IEnumerable GetApplicationTrees(string applicationAlias); + + /// + /// Gets the application tree for the applcation with the specified alias + /// + /// The application alias. + /// + /// Returns a ApplicationTree Array + IEnumerable GetApplicationTrees(string applicationAlias, bool onlyInitialized); + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IEntityService.cs b/src/Umbraco.Core/Services/IEntityService.cs index e7cbece1e1..fdce1751bf 100644 --- a/src/Umbraco.Core/Services/IEntityService.cs +++ b/src/Umbraco.Core/Services/IEntityService.cs @@ -1,182 +1,182 @@ -using System; -using System.Collections.Generic; -using Umbraco.Core.Models; -using Umbraco.Core.Models.EntityBase; - -namespace Umbraco.Core.Services -{ - public interface IEntityService - { - - /// - /// Gets an UmbracoEntity by its Id, and optionally loads the complete object graph. - /// - /// - /// By default this will load the base type with a minimum set of properties. - /// - /// Unique Id of the object to retrieve - /// Optional bool to load the complete object graph when set to False. - /// An - IUmbracoEntity GetByKey(Guid key, bool loadBaseType = true); - - /// - /// Gets an UmbracoEntity by its Id, and optionally loads the complete object graph. - /// - /// - /// By default this will load the base type with a minimum set of properties. - /// - /// Id of the object to retrieve - /// Optional bool to load the complete object graph when set to False. - /// An - IUmbracoEntity Get(int id, bool loadBaseType = true); - - /// - /// Gets an UmbracoEntity by its Id and UmbracoObjectType, and optionally loads the complete object graph. - /// - /// - /// By default this will load the base type with a minimum set of properties. - /// - /// Unique Id of the object to retrieve - /// UmbracoObjectType of the entity to retrieve - /// Optional bool to load the complete object graph when set to False. - /// An - IUmbracoEntity GetByKey(Guid key, UmbracoObjectTypes umbracoObjectType, bool loadBaseType = true); - - /// - /// Gets an UmbracoEntity by its Id and UmbracoObjectType, and optionally loads the complete object graph. - /// - /// - /// By default this will load the base type with a minimum set of properties. - /// - /// Id of the object to retrieve - /// UmbracoObjectType of the entity to retrieve - /// Optional bool to load the complete object graph when set to False. - /// An - IUmbracoEntity Get(int id, UmbracoObjectTypes umbracoObjectType, bool loadBaseType = true); - - /// - /// Gets an UmbracoEntity by its Id and specified Type. Optionally loads the complete object graph. - /// - /// - /// By default this will load the base type with a minimum set of properties. - /// - /// Type of the model to retrieve. Must be based on an - /// Unique Id of the object to retrieve - /// Optional bool to load the complete object graph when set to False. - /// An - IUmbracoEntity GetByKey(Guid key, bool loadBaseType = true) where T : IUmbracoEntity; - - /// - /// Gets an UmbracoEntity by its Id and specified Type. Optionally loads the complete object graph. - /// - /// - /// By default this will load the base type with a minimum set of properties. - /// - /// Type of the model to retrieve. Must be based on an - /// Id of the object to retrieve - /// Optional bool to load the complete object graph when set to False. - /// An - IUmbracoEntity Get(int id, bool loadBaseType = true) where T : IUmbracoEntity; - - /// - /// Gets the parent of entity by its id - /// - /// Id of the entity to retrieve the Parent for - /// An - IUmbracoEntity GetParent(int id); - - /// - /// Gets the parent of entity by its id and UmbracoObjectType - /// - /// Id of the entity to retrieve the Parent for - /// UmbracoObjectType of the parent to retrieve - /// An - IUmbracoEntity GetParent(int id, UmbracoObjectTypes umbracoObjectType); - - /// - /// Gets a collection of children by the parents Id - /// - /// Id of the parent to retrieve children for - /// An enumerable list of objects - IEnumerable GetChildren(int parentId); - - /// - /// Gets a collection of children by the parents Id and UmbracoObjectType - /// - /// Id of the parent to retrieve children for - /// UmbracoObjectType of the children to retrieve - /// An enumerable list of objects - IEnumerable GetChildren(int parentId, UmbracoObjectTypes umbracoObjectType); - - /// - /// Gets a collection of descendents by the parents Id - /// - /// Id of entity to retrieve descendents for - /// An enumerable list of objects - IEnumerable GetDescendents(int id); - - /// - /// Gets a collection of descendents by the parents Id - /// - /// Id of entity to retrieve descendents for - /// UmbracoObjectType of the descendents to retrieve - /// An enumerable list of objects - IEnumerable GetDescendents(int id, UmbracoObjectTypes umbracoObjectType); - - /// - /// Gets a collection of the entities at the root, which corresponds to the entities with a Parent Id of -1. - /// - /// UmbracoObjectType of the root entities to retrieve - /// An enumerable list of objects - IEnumerable GetRootEntities(UmbracoObjectTypes umbracoObjectType); - - /// - /// Gets a collection of all of a given type. - /// - /// Type of the entities to retrieve - /// An enumerable list of objects - IEnumerable GetAll() where T : IUmbracoEntity; - - /// - /// Gets a collection of all of a given type. - /// - /// UmbracoObjectType of the entities to return - /// An enumerable list of objects - IEnumerable GetAll(UmbracoObjectTypes umbracoObjectType); - - /// - /// Gets a collection of - /// - /// Guid id of the UmbracoObjectType - /// An enumerable list of objects - IEnumerable GetAll(Guid objectTypeId); - - /// - /// Gets the UmbracoObjectType from the integer id of an IUmbracoEntity. - /// - /// Id of the entity - /// - UmbracoObjectTypes GetObjectType(int id); - - /// - /// Gets the UmbracoObjectType from an IUmbracoEntity. - /// - /// - /// - UmbracoObjectTypes GetObjectType(IUmbracoEntity entity); - - /// - /// Gets the Type of an entity by its Id - /// - /// Id of the entity - /// Type of the entity - Type GetEntityType(int id); - - /// - /// Gets the Type of an entity by its - /// - /// - /// Type of the entity - Type GetEntityType(UmbracoObjectTypes umbracoObjectType); - } +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Services +{ + public interface IEntityService + { + + /// + /// Gets an UmbracoEntity by its Id, and optionally loads the complete object graph. + /// + /// + /// By default this will load the base type with a minimum set of properties. + /// + /// Unique Id of the object to retrieve + /// Optional bool to load the complete object graph when set to False. + /// An + IUmbracoEntity GetByKey(Guid key, bool loadBaseType = true); + + /// + /// Gets an UmbracoEntity by its Id, and optionally loads the complete object graph. + /// + /// + /// By default this will load the base type with a minimum set of properties. + /// + /// Id of the object to retrieve + /// Optional bool to load the complete object graph when set to False. + /// An + IUmbracoEntity Get(int id, bool loadBaseType = true); + + /// + /// Gets an UmbracoEntity by its Id and UmbracoObjectType, and optionally loads the complete object graph. + /// + /// + /// By default this will load the base type with a minimum set of properties. + /// + /// Unique Id of the object to retrieve + /// UmbracoObjectType of the entity to retrieve + /// Optional bool to load the complete object graph when set to False. + /// An + IUmbracoEntity GetByKey(Guid key, UmbracoObjectTypes umbracoObjectType, bool loadBaseType = true); + + /// + /// Gets an UmbracoEntity by its Id and UmbracoObjectType, and optionally loads the complete object graph. + /// + /// + /// By default this will load the base type with a minimum set of properties. + /// + /// Id of the object to retrieve + /// UmbracoObjectType of the entity to retrieve + /// Optional bool to load the complete object graph when set to False. + /// An + IUmbracoEntity Get(int id, UmbracoObjectTypes umbracoObjectType, bool loadBaseType = true); + + /// + /// Gets an UmbracoEntity by its Id and specified Type. Optionally loads the complete object graph. + /// + /// + /// By default this will load the base type with a minimum set of properties. + /// + /// Type of the model to retrieve. Must be based on an + /// Unique Id of the object to retrieve + /// Optional bool to load the complete object graph when set to False. + /// An + IUmbracoEntity GetByKey(Guid key, bool loadBaseType = true) where T : IUmbracoEntity; + + /// + /// Gets an UmbracoEntity by its Id and specified Type. Optionally loads the complete object graph. + /// + /// + /// By default this will load the base type with a minimum set of properties. + /// + /// Type of the model to retrieve. Must be based on an + /// Id of the object to retrieve + /// Optional bool to load the complete object graph when set to False. + /// An + IUmbracoEntity Get(int id, bool loadBaseType = true) where T : IUmbracoEntity; + + /// + /// Gets the parent of entity by its id + /// + /// Id of the entity to retrieve the Parent for + /// An + IUmbracoEntity GetParent(int id); + + /// + /// Gets the parent of entity by its id and UmbracoObjectType + /// + /// Id of the entity to retrieve the Parent for + /// UmbracoObjectType of the parent to retrieve + /// An + IUmbracoEntity GetParent(int id, UmbracoObjectTypes umbracoObjectType); + + /// + /// Gets a collection of children by the parents Id + /// + /// Id of the parent to retrieve children for + /// An enumerable list of objects + IEnumerable GetChildren(int parentId); + + /// + /// Gets a collection of children by the parents Id and UmbracoObjectType + /// + /// Id of the parent to retrieve children for + /// UmbracoObjectType of the children to retrieve + /// An enumerable list of objects + IEnumerable GetChildren(int parentId, UmbracoObjectTypes umbracoObjectType); + + /// + /// Gets a collection of descendents by the parents Id + /// + /// Id of entity to retrieve descendents for + /// An enumerable list of objects + IEnumerable GetDescendents(int id); + + /// + /// Gets a collection of descendents by the parents Id + /// + /// Id of entity to retrieve descendents for + /// UmbracoObjectType of the descendents to retrieve + /// An enumerable list of objects + IEnumerable GetDescendents(int id, UmbracoObjectTypes umbracoObjectType); + + /// + /// Gets a collection of the entities at the root, which corresponds to the entities with a Parent Id of -1. + /// + /// UmbracoObjectType of the root entities to retrieve + /// An enumerable list of objects + IEnumerable GetRootEntities(UmbracoObjectTypes umbracoObjectType); + + /// + /// Gets a collection of all of a given type. + /// + /// Type of the entities to retrieve + /// An enumerable list of objects + IEnumerable GetAll() where T : IUmbracoEntity; + + /// + /// Gets a collection of all of a given type. + /// + /// UmbracoObjectType of the entities to return + /// An enumerable list of objects + IEnumerable GetAll(UmbracoObjectTypes umbracoObjectType); + + /// + /// Gets a collection of + /// + /// Guid id of the UmbracoObjectType + /// An enumerable list of objects + IEnumerable GetAll(Guid objectTypeId); + + /// + /// Gets the UmbracoObjectType from the integer id of an IUmbracoEntity. + /// + /// Id of the entity + /// + UmbracoObjectTypes GetObjectType(int id); + + /// + /// Gets the UmbracoObjectType from an IUmbracoEntity. + /// + /// + /// + UmbracoObjectTypes GetObjectType(IUmbracoEntity entity); + + /// + /// Gets the Type of an entity by its Id + /// + /// Id of the entity + /// Type of the entity + Type GetEntityType(int id); + + /// + /// Gets the Type of an entity by its + /// + /// + /// Type of the entity + Type GetEntityType(UmbracoObjectTypes umbracoObjectType); + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IMacroService.cs b/src/Umbraco.Core/Services/IMacroService.cs index 4672f44087..a123fb063e 100644 --- a/src/Umbraco.Core/Services/IMacroService.cs +++ b/src/Umbraco.Core/Services/IMacroService.cs @@ -1,57 +1,57 @@ -using System.Collections.Generic; -using Umbraco.Core.Models; - -namespace Umbraco.Core.Services -{ - /// - /// Defines the MacroService, which is an easy access to operations involving - /// - public interface IMacroService : IService - { - - /// - /// Gets an object by its alias - /// - /// Alias to retrieve an for - /// An object - IMacro GetByAlias(string alias); - - ///// - ///// Gets a list all available objects - ///// - ///// Optional array of aliases to limit the results - ///// An enumerable list of objects - //IEnumerable GetAll(params string[] aliases); - - IEnumerable GetAll(params int[] ids); - - IMacro GetById(int id); - - /// - /// Deletes an - /// - /// to delete - /// Optional id of the user deleting the macro - void Delete(IMacro macro, int userId = 0); - - /// - /// Saves an - /// - /// to save - /// Optional id of the user saving the macro - void Save(IMacro macro, int userId = 0); - - ///// - ///// Gets a list all available plugins - ///// - ///// An enumerable list of objects - //IEnumerable GetMacroPropertyTypes(); - - ///// - ///// Gets an by its alias - ///// - ///// Alias to retrieve an for - ///// An object - //IMacroPropertyType GetMacroPropertyTypeByAlias(string alias); - } +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Services +{ + /// + /// Defines the MacroService, which is an easy access to operations involving + /// + public interface IMacroService : IService + { + + /// + /// Gets an object by its alias + /// + /// Alias to retrieve an for + /// An object + IMacro GetByAlias(string alias); + + ///// + ///// Gets a list all available objects + ///// + ///// Optional array of aliases to limit the results + ///// An enumerable list of objects + //IEnumerable GetAll(params string[] aliases); + + IEnumerable GetAll(params int[] ids); + + IMacro GetById(int id); + + /// + /// Deletes an + /// + /// to delete + /// Optional id of the user deleting the macro + void Delete(IMacro macro, int userId = 0); + + /// + /// Saves an + /// + /// to save + /// Optional id of the user saving the macro + void Save(IMacro macro, int userId = 0); + + ///// + ///// Gets a list all available plugins + ///// + ///// An enumerable list of objects + //IEnumerable GetMacroPropertyTypes(); + + ///// + ///// Gets an by its alias + ///// + ///// Alias to retrieve an for + ///// An object + //IMacroPropertyType GetMacroPropertyTypeByAlias(string alias); + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IMemberTypeService.cs b/src/Umbraco.Core/Services/IMemberTypeService.cs index 7857e1dda0..e456653193 100644 --- a/src/Umbraco.Core/Services/IMemberTypeService.cs +++ b/src/Umbraco.Core/Services/IMemberTypeService.cs @@ -1,29 +1,29 @@ -using System.Collections.Generic; -using Umbraco.Core.Models; - -namespace Umbraco.Core.Services -{ - internal interface IMemberTypeService : IService - { - /// - /// Gets a list of all available objects - /// - /// Optional list of ids - /// An Enumerable list of objects - IEnumerable GetAllMemberTypes(params int[] ids); - - /// - /// Gets an object by its Id - /// - /// Id of the to retrieve - /// - IMemberType GetMemberType(int id); - - /// - /// Gets an object by its Alias - /// - /// Alias of the to retrieve - /// - IMemberType GetMemberType(string alias); - } +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Services +{ + internal interface IMemberTypeService : IService + { + /// + /// Gets a list of all available objects + /// + /// Optional list of ids + /// An Enumerable list of objects + IEnumerable GetAllMemberTypes(params int[] ids); + + /// + /// Gets an object by its Id + /// + /// Id of the to retrieve + /// + IMemberType GetMemberType(int id); + + /// + /// Gets an object by its Alias + /// + /// Alias of the to retrieve + /// + IMemberType GetMemberType(string alias); + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IRelationService.cs b/src/Umbraco.Core/Services/IRelationService.cs index 32e7d88f2a..3383a02212 100644 --- a/src/Umbraco.Core/Services/IRelationService.cs +++ b/src/Umbraco.Core/Services/IRelationService.cs @@ -1,215 +1,215 @@ -using System; -using System.Collections.Generic; -using Umbraco.Core.Models; -using Umbraco.Core.Models.EntityBase; - -namespace Umbraco.Core.Services -{ - public interface IRelationService - { - /// - /// Gets a by its Id - /// - /// Id of the - /// A object - IRelation GetById(int id); - - /// - /// Gets a by its Id - /// - /// Id of the - /// A object - IRelationType GetRelationTypeById(int id); - - /// - /// Gets a by its Alias - /// - /// Alias of the - /// A object - IRelationType GetRelationTypeByAlias(string alias); - - /// - /// Gets all objects - /// - /// Optional array of integer ids to return relations for - /// An enumerable list of objects - IEnumerable GetAllRelations(params int[] ids); - - /// - /// Gets all objects by their - /// - /// to retrieve Relations for - /// An enumerable list of objects - IEnumerable GetAllRelationsByRelationType(RelationType relationType); - - /// - /// Gets all objects by their 's Id - /// - /// Id of the to retrieve Relations for - /// An enumerable list of objects - IEnumerable GetAllRelationsByRelationType(int relationTypeId); - - /// - /// Gets all objects - /// - /// Optional array of integer ids to return relationtypes for - /// An enumerable list of objects - IEnumerable GetAllRelationTypes(params int[] ids); - - /// - /// Gets a list of objects by their parent Id - /// - /// Id of the parent to retrieve relations for - /// An enumerable list of objects - IEnumerable GetByParentId(int id); - - /// - /// Gets a list of objects by their child Id - /// - /// Id of the child to retrieve relations for - /// An enumerable list of objects - IEnumerable GetByChildId(int id); - - /// - /// Gets a list of objects by their child or parent Id. - /// Using this method will get you all relations regards of it being a child or parent relation. - /// - /// Id of the child or parent to retrieve relations for - /// An enumerable list of objects - IEnumerable GetByParentOrChildId(int id); - - /// - /// Gets a list of objects by the Name of the - /// - /// Name of the to retrieve Relations for - /// An enumerable list of objects - IEnumerable GetByRelationTypeName(string relationTypeName); - - /// - /// Gets a list of objects by the Alias of the - /// - /// Alias of the to retrieve Relations for - /// An enumerable list of objects - IEnumerable GetByRelationTypeAlias(string relationTypeAlias); - - /// - /// Gets a list of objects by the Id of the - /// - /// Id of the to retrieve Relations for - /// An enumerable list of objects - IEnumerable GetByRelationTypeId(int relationTypeId); - - /// - /// Gets the Child object from a Relation as an - /// - /// Relation to retrieve child object from - /// Optional bool to load the complete object graph when set to False - /// An - IUmbracoEntity GetChildEntityFromRelation(IRelation relation, bool loadBaseType = false); - - /// - /// Gets the Parent object from a Relation as an - /// - /// Relation to retrieve parent object from - /// Optional bool to load the complete object graph when set to False - /// An - IUmbracoEntity GetParentEntityFromRelation(IRelation relation, bool loadBaseType = false); - - /// - /// Gets the Parent and Child objects from a Relation as a "/> with . - /// - /// Relation to retrieve parent and child object from - /// Optional bool to load the complete object graph when set to False - /// Returns a Tuple with Parent (item1) and Child (item2) - Tuple GetEntitiesFromRelation(IRelation relation, bool loadBaseType = false); - - /// - /// Gets the Child objects from a list of Relations as a list of objects. - /// - /// List of relations to retrieve child objects from - /// Optional bool to load the complete object graph when set to False - /// An enumerable list of - IEnumerable GetChildEntitiesFromRelations(IEnumerable relations, bool loadBaseType = false); - - /// - /// Gets the Parent objects from a list of Relations as a list of objects. - /// - /// List of relations to retrieve parent objects from - /// Optional bool to load the complete object graph when set to False - /// An enumerable list of - IEnumerable GetParentEntitiesFromRelations(IEnumerable relations, - bool loadBaseType = false); - - /// - /// Gets the Parent and Child objects from a list of Relations as a list of objects. - /// - /// List of relations to retrieve parent and child objects from - /// Optional bool to load the complete object graph when set to False - /// An enumerable list of with - IEnumerable> GetEntitiesFromRelations( - IEnumerable relations, - bool loadBaseType = false); - - /// - /// Relates two objects that are based on the interface. - /// - /// Parent entity - /// Child entity - /// The type of relation to create - /// The created - IRelation Relate(IUmbracoEntity parent, IUmbracoEntity child, IRelationType relationType); - - /// - /// Relates two objects that are based on the interface. - /// - /// Parent entity - /// Child entity - /// Alias of the type of relation to create - /// The created - IRelation Relate(IUmbracoEntity parent, IUmbracoEntity child, string relationTypeAlias); - - /// - /// Checks whether any relations exists for the passed in . - /// - /// to check for relations - /// Returns True if any relations exists for the given , otherwise False - bool HasRelations(IRelationType relationType); - - /// - /// Checks whether any relations exists for the passed in Id. - /// - /// Id of an object to check relations for - /// Returns True if any relations exists with the given Id, otherwise False - bool IsRelated(int id); - - /// - /// Saves a - /// - /// Relation to save - void Save(IRelation relation); - - /// - /// Saves a - /// - /// RelationType to Save - void Save(IRelationType relationType); - - /// - /// Deletes a - /// - /// Relation to Delete - void Delete(IRelation relation); - - /// - /// Deletes a - /// - /// RelationType to Delete - void Delete(IRelationType relationType); - - /// - /// Deletes all objects based on the passed in - /// - /// to Delete Relations for - void DeleteRelationsOfType(IRelationType relationType); - } +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Services +{ + public interface IRelationService + { + /// + /// Gets a by its Id + /// + /// Id of the + /// A object + IRelation GetById(int id); + + /// + /// Gets a by its Id + /// + /// Id of the + /// A object + IRelationType GetRelationTypeById(int id); + + /// + /// Gets a by its Alias + /// + /// Alias of the + /// A object + IRelationType GetRelationTypeByAlias(string alias); + + /// + /// Gets all objects + /// + /// Optional array of integer ids to return relations for + /// An enumerable list of objects + IEnumerable GetAllRelations(params int[] ids); + + /// + /// Gets all objects by their + /// + /// to retrieve Relations for + /// An enumerable list of objects + IEnumerable GetAllRelationsByRelationType(RelationType relationType); + + /// + /// Gets all objects by their 's Id + /// + /// Id of the to retrieve Relations for + /// An enumerable list of objects + IEnumerable GetAllRelationsByRelationType(int relationTypeId); + + /// + /// Gets all objects + /// + /// Optional array of integer ids to return relationtypes for + /// An enumerable list of objects + IEnumerable GetAllRelationTypes(params int[] ids); + + /// + /// Gets a list of objects by their parent Id + /// + /// Id of the parent to retrieve relations for + /// An enumerable list of objects + IEnumerable GetByParentId(int id); + + /// + /// Gets a list of objects by their child Id + /// + /// Id of the child to retrieve relations for + /// An enumerable list of objects + IEnumerable GetByChildId(int id); + + /// + /// Gets a list of objects by their child or parent Id. + /// Using this method will get you all relations regards of it being a child or parent relation. + /// + /// Id of the child or parent to retrieve relations for + /// An enumerable list of objects + IEnumerable GetByParentOrChildId(int id); + + /// + /// Gets a list of objects by the Name of the + /// + /// Name of the to retrieve Relations for + /// An enumerable list of objects + IEnumerable GetByRelationTypeName(string relationTypeName); + + /// + /// Gets a list of objects by the Alias of the + /// + /// Alias of the to retrieve Relations for + /// An enumerable list of objects + IEnumerable GetByRelationTypeAlias(string relationTypeAlias); + + /// + /// Gets a list of objects by the Id of the + /// + /// Id of the to retrieve Relations for + /// An enumerable list of objects + IEnumerable GetByRelationTypeId(int relationTypeId); + + /// + /// Gets the Child object from a Relation as an + /// + /// Relation to retrieve child object from + /// Optional bool to load the complete object graph when set to False + /// An + IUmbracoEntity GetChildEntityFromRelation(IRelation relation, bool loadBaseType = false); + + /// + /// Gets the Parent object from a Relation as an + /// + /// Relation to retrieve parent object from + /// Optional bool to load the complete object graph when set to False + /// An + IUmbracoEntity GetParentEntityFromRelation(IRelation relation, bool loadBaseType = false); + + /// + /// Gets the Parent and Child objects from a Relation as a "/> with . + /// + /// Relation to retrieve parent and child object from + /// Optional bool to load the complete object graph when set to False + /// Returns a Tuple with Parent (item1) and Child (item2) + Tuple GetEntitiesFromRelation(IRelation relation, bool loadBaseType = false); + + /// + /// Gets the Child objects from a list of Relations as a list of objects. + /// + /// List of relations to retrieve child objects from + /// Optional bool to load the complete object graph when set to False + /// An enumerable list of + IEnumerable GetChildEntitiesFromRelations(IEnumerable relations, bool loadBaseType = false); + + /// + /// Gets the Parent objects from a list of Relations as a list of objects. + /// + /// List of relations to retrieve parent objects from + /// Optional bool to load the complete object graph when set to False + /// An enumerable list of + IEnumerable GetParentEntitiesFromRelations(IEnumerable relations, + bool loadBaseType = false); + + /// + /// Gets the Parent and Child objects from a list of Relations as a list of objects. + /// + /// List of relations to retrieve parent and child objects from + /// Optional bool to load the complete object graph when set to False + /// An enumerable list of with + IEnumerable> GetEntitiesFromRelations( + IEnumerable relations, + bool loadBaseType = false); + + /// + /// Relates two objects that are based on the interface. + /// + /// Parent entity + /// Child entity + /// The type of relation to create + /// The created + IRelation Relate(IUmbracoEntity parent, IUmbracoEntity child, IRelationType relationType); + + /// + /// Relates two objects that are based on the interface. + /// + /// Parent entity + /// Child entity + /// Alias of the type of relation to create + /// The created + IRelation Relate(IUmbracoEntity parent, IUmbracoEntity child, string relationTypeAlias); + + /// + /// Checks whether any relations exists for the passed in . + /// + /// to check for relations + /// Returns True if any relations exists for the given , otherwise False + bool HasRelations(IRelationType relationType); + + /// + /// Checks whether any relations exists for the passed in Id. + /// + /// Id of an object to check relations for + /// Returns True if any relations exists with the given Id, otherwise False + bool IsRelated(int id); + + /// + /// Saves a + /// + /// Relation to save + void Save(IRelation relation); + + /// + /// Saves a + /// + /// RelationType to Save + void Save(IRelationType relationType); + + /// + /// Deletes a + /// + /// Relation to Delete + void Delete(IRelation relation); + + /// + /// Deletes a + /// + /// RelationType to Delete + void Delete(IRelationType relationType); + + /// + /// Deletes all objects based on the passed in + /// + /// to Delete Relations for + void DeleteRelationsOfType(IRelationType relationType); + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ISectionService.cs b/src/Umbraco.Core/Services/ISectionService.cs index 1df38977f0..bc950b9470 100644 --- a/src/Umbraco.Core/Services/ISectionService.cs +++ b/src/Umbraco.Core/Services/ISectionService.cs @@ -1,55 +1,55 @@ -using System.Collections.Generic; -using Umbraco.Core.Models; - -namespace Umbraco.Core.Services -{ - public interface ISectionService - { - /// - /// Ensures all available sections exist in the storage medium - /// - /// - void Initialize(IEnumerable
existingSections); - - /// - /// The cache storage for all applications - /// - IEnumerable
GetSections(); - - /// - /// Get the user's allowed sections - /// - /// - /// - IEnumerable
GetAllowedSections(int userId); - - /// - /// Gets the application by its alias. - /// - /// The application alias. - /// - Section GetByAlias(string appAlias); - - /// - /// Creates a new applcation if no application with the specified alias is found. - /// - /// The application name. - /// The application alias. - /// The application icon, which has to be located in umbraco/images/tray folder. - void MakeNew(string name, string alias, string icon); - - /// - /// Makes the new. - /// - /// The name. - /// The alias. - /// The icon. - /// The sort order. - void MakeNew(string name, string alias, string icon, int sortOrder); - - /// - /// Deletes the section - /// - void DeleteSection(Section section); - } +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Services +{ + public interface ISectionService + { + /// + /// Ensures all available sections exist in the storage medium + /// + /// + void Initialize(IEnumerable
existingSections); + + /// + /// The cache storage for all applications + /// + IEnumerable
GetSections(); + + /// + /// Get the user's allowed sections + /// + /// + /// + IEnumerable
GetAllowedSections(int userId); + + /// + /// Gets the application by its alias. + /// + /// The application alias. + /// + Section GetByAlias(string appAlias); + + /// + /// Creates a new applcation if no application with the specified alias is found. + /// + /// The application name. + /// The application alias. + /// The application icon, which has to be located in umbraco/images/tray folder. + void MakeNew(string name, string alias, string icon); + + /// + /// Makes the new. + /// + /// The name. + /// The alias. + /// The icon. + /// The sort order. + void MakeNew(string name, string alias, string icon, int sortOrder); + + /// + /// Deletes the section + /// + void DeleteSection(Section section); + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ITagService.cs b/src/Umbraco.Core/Services/ITagService.cs index aa255e60f9..efba465c21 100644 --- a/src/Umbraco.Core/Services/ITagService.cs +++ b/src/Umbraco.Core/Services/ITagService.cs @@ -1,64 +1,64 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using Umbraco.Core.Models; - -namespace Umbraco.Core.Services -{ - /// - /// Tag service to query for tags in the tags db table. The tags returned are only relavent for published content & saved media or members - /// - /// - /// If there is unpublished content with tags, those tags will not be contained. - /// - /// This service does not contain methods to query for content, media or members based on tags, those methods will be added - /// to the content, media and member services respectively. - /// - public interface ITagService : IService - { - - /// - /// Get every tag stored in the database (with optional group) - /// - IEnumerable GetAllTags(string group = null); - - /// - /// Get all tags for content items (with optional group) - /// - /// - /// - IEnumerable GetAllContentTags(string group = null); - - /// - /// Get all tags for media items (with optional group) - /// - /// - /// - IEnumerable GetAllMediaTags(string group = null); - - /// - /// Get all tags for member items (with optional group) - /// - /// - /// - IEnumerable GetAllMemberTags(string group = null); - - /// - /// Returns all tags attached to a property by entity id - /// - /// - /// - /// - /// - IEnumerable GetTagsForProperty(int contentId, string propertyTypeAlias, string tagGroup = null); - - /// - /// Returns all tags attached to an entity (content, media or member) by entity id - /// - /// - /// - /// - IEnumerable GetTagsForEntity(int contentId, string tagGroup = null); - - } +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Services +{ + /// + /// Tag service to query for tags in the tags db table. The tags returned are only relavent for published content & saved media or members + /// + /// + /// If there is unpublished content with tags, those tags will not be contained. + /// + /// This service does not contain methods to query for content, media or members based on tags, those methods will be added + /// to the content, media and member services respectively. + /// + public interface ITagService : IService + { + + /// + /// Get every tag stored in the database (with optional group) + /// + IEnumerable GetAllTags(string group = null); + + /// + /// Get all tags for content items (with optional group) + /// + /// + /// + IEnumerable GetAllContentTags(string group = null); + + /// + /// Get all tags for media items (with optional group) + /// + /// + /// + IEnumerable GetAllMediaTags(string group = null); + + /// + /// Get all tags for member items (with optional group) + /// + /// + /// + IEnumerable GetAllMemberTags(string group = null); + + /// + /// Returns all tags attached to a property by entity id + /// + /// + /// + /// + /// + IEnumerable GetTagsForProperty(int contentId, string propertyTypeAlias, string tagGroup = null); + + /// + /// Returns all tags attached to an entity (content, media or member) by entity id + /// + /// + /// + /// + IEnumerable GetTagsForEntity(int contentId, string tagGroup = null); + + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/MacroService.cs b/src/Umbraco.Core/Services/MacroService.cs index 6d3b18010f..f6aa425c1b 100644 --- a/src/Umbraco.Core/Services/MacroService.cs +++ b/src/Umbraco.Core/Services/MacroService.cs @@ -1,213 +1,213 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using Umbraco.Core.Auditing; -using Umbraco.Core.Events; -using Umbraco.Core.IO; -using Umbraco.Core.Models; -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.Repositories; -using Umbraco.Core.Persistence.UnitOfWork; - -namespace Umbraco.Core.Services -{ - /// - /// Represents the Macro Service, which is an easy access to operations involving - /// - internal class MacroService : IMacroService - { - private readonly RepositoryFactory _repositoryFactory; - private readonly IDatabaseUnitOfWorkProvider _uowProvider; - - public MacroService() - : this(new PetaPocoUnitOfWorkProvider(), new RepositoryFactory()) - { - } - - public MacroService(IDatabaseUnitOfWorkProvider provider) - : this(provider, new RepositoryFactory()) - { - } - - public MacroService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory) - { - _uowProvider = provider; - _repositoryFactory = repositoryFactory; - } - - /// - /// Returns an enum based on the properties on the Macro - /// - /// - internal static MacroTypes GetMacroType(IMacro macro) - { - if (string.IsNullOrEmpty(macro.XsltPath) == false) - return MacroTypes.Xslt; - - if (string.IsNullOrEmpty(macro.ScriptPath) == false) - { - //we need to check if the file path saved is a virtual path starting with ~/Views/MacroPartials, if so then this is - //a partial view macro, not a script macro - //we also check if the file exists in ~/App_Plugins/[Packagename]/Views/MacroPartials, if so then it is also a partial view. - return (macro.ScriptPath.InvariantStartsWith(SystemDirectories.MvcViews + "/MacroPartials/") - || (Regex.IsMatch(macro.ScriptPath, "~/App_Plugins/.+?/Views/MacroPartials", RegexOptions.Compiled | RegexOptions.IgnoreCase))) - ? MacroTypes.PartialView - : MacroTypes.Script; - } - - if (string.IsNullOrEmpty(macro.ControlType) == false && macro.ControlType.InvariantContains(".ascx")) - return MacroTypes.UserControl; - - if (string.IsNullOrEmpty(macro.ControlType) == false && string.IsNullOrEmpty(macro.ControlAssembly) == false) - return MacroTypes.CustomControl; - - return MacroTypes.Unknown; - } - - /// - /// Gets an object by its alias - /// - /// Alias to retrieve an for - /// An object - public IMacro GetByAlias(string alias) - { - using (var repository = _repositoryFactory.CreateMacroRepository(_uowProvider.GetUnitOfWork())) - { - var q = new Query(); - q.Where(macro => macro.Alias == alias); - return repository.GetByQuery(q).FirstOrDefault(); - } - } - - ///// - ///// Gets a list all available objects - ///// - ///// Optional array of aliases to limit the results - ///// An enumerable list of objects - //public IEnumerable GetAll(params string[] aliases) - //{ - // using (var repository = _repositoryFactory.CreateMacroRepository(_uowProvider.GetUnitOfWork())) - // { - // if (aliases.Any()) - // { - // return GetAllByAliases(repository, aliases); - // } - - // return repository.GetAll(); - // } - //} - - public IEnumerable GetAll(params int[] ids) - { - using (var repository = _repositoryFactory.CreateMacroRepository(_uowProvider.GetUnitOfWork())) - { - return repository.GetAll(ids); - } - } - - public IMacro GetById(int id) - { - using (var repository = _repositoryFactory.CreateMacroRepository(_uowProvider.GetUnitOfWork())) - { - return repository.Get(id); - } - } - - //private IEnumerable GetAllByAliases(IMacroRepository repo, IEnumerable aliases) - //{ - // foreach (var alias in aliases) - // { - // var q = new Query(); - // q.Where(macro => macro.Alias == alias); - // yield return repo.GetByQuery(q).FirstOrDefault(); - // } - //} - - /// - /// Deletes an - /// - /// to delete - /// Optional id of the user deleting the macro - public void Delete(IMacro macro, int userId = 0) - { - if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(macro), this)) - return; - - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateMacroRepository(uow)) - { - repository.Delete(macro); - uow.Commit(); - - Deleted.RaiseEvent(new DeleteEventArgs(macro, false), this); - } - - Audit.Add(AuditTypes.Delete, "Delete Macro performed by user", userId, -1); - } - - /// - /// Saves an - /// - /// to save - /// Optional Id of the user deleting the macro - public void Save(IMacro macro, int userId = 0) - { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(macro), this)) - return; - - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateMacroRepository(uow)) - { - repository.AddOrUpdate(macro); - uow.Commit(); - - Saved.RaiseEvent(new SaveEventArgs(macro, false), this); - } - - Audit.Add(AuditTypes.Save, "Save Macro performed by user", userId, -1); - } - - ///// - ///// Gets a list all available plugins - ///// - ///// An enumerable list of objects - //public IEnumerable GetMacroPropertyTypes() - //{ - // return MacroPropertyTypeResolver.Current.MacroPropertyTypes; - //} - - ///// - ///// Gets an by its alias - ///// - ///// Alias to retrieve an for - ///// An object - //public IMacroPropertyType GetMacroPropertyTypeByAlias(string alias) - //{ - // return MacroPropertyTypeResolver.Current.MacroPropertyTypes.FirstOrDefault(x => x.Alias == alias); - //} - - #region Event Handlers - /// - /// Occurs before Delete - /// - public static event TypedEventHandler> Deleting; - - /// - /// Occurs after Delete - /// - public static event TypedEventHandler> Deleted; - - /// - /// Occurs before Save - /// - public static event TypedEventHandler> Saving; - - /// - /// Occurs after Save - /// - public static event TypedEventHandler> Saved; - #endregion - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using Umbraco.Core.Auditing; +using Umbraco.Core.Events; +using Umbraco.Core.IO; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.UnitOfWork; + +namespace Umbraco.Core.Services +{ + /// + /// Represents the Macro Service, which is an easy access to operations involving + /// + internal class MacroService : IMacroService + { + private readonly RepositoryFactory _repositoryFactory; + private readonly IDatabaseUnitOfWorkProvider _uowProvider; + + public MacroService() + : this(new PetaPocoUnitOfWorkProvider(), new RepositoryFactory()) + { + } + + public MacroService(IDatabaseUnitOfWorkProvider provider) + : this(provider, new RepositoryFactory()) + { + } + + public MacroService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory) + { + _uowProvider = provider; + _repositoryFactory = repositoryFactory; + } + + /// + /// Returns an enum based on the properties on the Macro + /// + /// + internal static MacroTypes GetMacroType(IMacro macro) + { + if (string.IsNullOrEmpty(macro.XsltPath) == false) + return MacroTypes.Xslt; + + if (string.IsNullOrEmpty(macro.ScriptPath) == false) + { + //we need to check if the file path saved is a virtual path starting with ~/Views/MacroPartials, if so then this is + //a partial view macro, not a script macro + //we also check if the file exists in ~/App_Plugins/[Packagename]/Views/MacroPartials, if so then it is also a partial view. + return (macro.ScriptPath.InvariantStartsWith(SystemDirectories.MvcViews + "/MacroPartials/") + || (Regex.IsMatch(macro.ScriptPath, "~/App_Plugins/.+?/Views/MacroPartials", RegexOptions.Compiled | RegexOptions.IgnoreCase))) + ? MacroTypes.PartialView + : MacroTypes.Script; + } + + if (string.IsNullOrEmpty(macro.ControlType) == false && macro.ControlType.InvariantContains(".ascx")) + return MacroTypes.UserControl; + + if (string.IsNullOrEmpty(macro.ControlType) == false && string.IsNullOrEmpty(macro.ControlAssembly) == false) + return MacroTypes.CustomControl; + + return MacroTypes.Unknown; + } + + /// + /// Gets an object by its alias + /// + /// Alias to retrieve an for + /// An object + public IMacro GetByAlias(string alias) + { + using (var repository = _repositoryFactory.CreateMacroRepository(_uowProvider.GetUnitOfWork())) + { + var q = new Query(); + q.Where(macro => macro.Alias == alias); + return repository.GetByQuery(q).FirstOrDefault(); + } + } + + ///// + ///// Gets a list all available objects + ///// + ///// Optional array of aliases to limit the results + ///// An enumerable list of objects + //public IEnumerable GetAll(params string[] aliases) + //{ + // using (var repository = _repositoryFactory.CreateMacroRepository(_uowProvider.GetUnitOfWork())) + // { + // if (aliases.Any()) + // { + // return GetAllByAliases(repository, aliases); + // } + + // return repository.GetAll(); + // } + //} + + public IEnumerable GetAll(params int[] ids) + { + using (var repository = _repositoryFactory.CreateMacroRepository(_uowProvider.GetUnitOfWork())) + { + return repository.GetAll(ids); + } + } + + public IMacro GetById(int id) + { + using (var repository = _repositoryFactory.CreateMacroRepository(_uowProvider.GetUnitOfWork())) + { + return repository.Get(id); + } + } + + //private IEnumerable GetAllByAliases(IMacroRepository repo, IEnumerable aliases) + //{ + // foreach (var alias in aliases) + // { + // var q = new Query(); + // q.Where(macro => macro.Alias == alias); + // yield return repo.GetByQuery(q).FirstOrDefault(); + // } + //} + + /// + /// Deletes an + /// + /// to delete + /// Optional id of the user deleting the macro + public void Delete(IMacro macro, int userId = 0) + { + if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(macro), this)) + return; + + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMacroRepository(uow)) + { + repository.Delete(macro); + uow.Commit(); + + Deleted.RaiseEvent(new DeleteEventArgs(macro, false), this); + } + + Audit.Add(AuditTypes.Delete, "Delete Macro performed by user", userId, -1); + } + + /// + /// Saves an + /// + /// to save + /// Optional Id of the user deleting the macro + public void Save(IMacro macro, int userId = 0) + { + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(macro), this)) + return; + + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMacroRepository(uow)) + { + repository.AddOrUpdate(macro); + uow.Commit(); + + Saved.RaiseEvent(new SaveEventArgs(macro, false), this); + } + + Audit.Add(AuditTypes.Save, "Save Macro performed by user", userId, -1); + } + + ///// + ///// Gets a list all available plugins + ///// + ///// An enumerable list of objects + //public IEnumerable GetMacroPropertyTypes() + //{ + // return MacroPropertyTypeResolver.Current.MacroPropertyTypes; + //} + + ///// + ///// Gets an by its alias + ///// + ///// Alias to retrieve an for + ///// An object + //public IMacroPropertyType GetMacroPropertyTypeByAlias(string alias) + //{ + // return MacroPropertyTypeResolver.Current.MacroPropertyTypes.FirstOrDefault(x => x.Alias == alias); + //} + + #region Event Handlers + /// + /// Occurs before Delete + /// + public static event TypedEventHandler> Deleting; + + /// + /// Occurs after Delete + /// + public static event TypedEventHandler> Deleted; + + /// + /// Occurs before Save + /// + public static event TypedEventHandler> Saving; + + /// + /// Occurs after Save + /// + public static event TypedEventHandler> Saved; + #endregion + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/MemberTypeService.cs b/src/Umbraco.Core/Services/MemberTypeService.cs index 81a231f7c5..4f3e09b224 100644 --- a/src/Umbraco.Core/Services/MemberTypeService.cs +++ b/src/Umbraco.Core/Services/MemberTypeService.cs @@ -1,70 +1,70 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core.Models; -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.UnitOfWork; - -namespace Umbraco.Core.Services -{ - internal class MemberTypeService : IMemberTypeService - { - private readonly IDatabaseUnitOfWorkProvider _uowProvider; - private readonly RepositoryFactory _repositoryFactory; - - public MemberTypeService() - : this(new PetaPocoUnitOfWorkProvider(), new RepositoryFactory()) - {} - - public MemberTypeService(RepositoryFactory repositoryFactory) - : this(new PetaPocoUnitOfWorkProvider(), repositoryFactory) - { } - - public MemberTypeService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory) - { - if (provider == null) throw new ArgumentNullException("provider"); - if (repositoryFactory == null) throw new ArgumentNullException("repositoryFactory"); - _uowProvider = provider; - _repositoryFactory = repositoryFactory; - } - - public IEnumerable GetAllMemberTypes(params int[] ids) - { - using (var repository = _repositoryFactory.CreateMemberTypeRepository(_uowProvider.GetUnitOfWork())) - { - return repository.GetAll(ids); - } - } - - /// - /// Gets an object by its Id - /// - /// Id of the to retrieve - /// - public IMemberType GetMemberType(int id) - { - using (var repository = _repositoryFactory.CreateMemberTypeRepository(_uowProvider.GetUnitOfWork())) - { - return repository.Get(id); - } - } - - /// - /// Gets an object by its Alias - /// - /// Alias of the to retrieve - /// - public IMemberType GetMemberType(string alias) - { - using (var repository = _repositoryFactory.CreateMemberTypeRepository(_uowProvider.GetUnitOfWork())) - { - var query = Query.Builder.Where(x => x.Alias == alias); - var contentTypes = repository.GetByQuery(query); - - return contentTypes.FirstOrDefault(); - } - } - - } +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.UnitOfWork; + +namespace Umbraco.Core.Services +{ + internal class MemberTypeService : IMemberTypeService + { + private readonly IDatabaseUnitOfWorkProvider _uowProvider; + private readonly RepositoryFactory _repositoryFactory; + + public MemberTypeService() + : this(new PetaPocoUnitOfWorkProvider(), new RepositoryFactory()) + {} + + public MemberTypeService(RepositoryFactory repositoryFactory) + : this(new PetaPocoUnitOfWorkProvider(), repositoryFactory) + { } + + public MemberTypeService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory) + { + if (provider == null) throw new ArgumentNullException("provider"); + if (repositoryFactory == null) throw new ArgumentNullException("repositoryFactory"); + _uowProvider = provider; + _repositoryFactory = repositoryFactory; + } + + public IEnumerable GetAllMemberTypes(params int[] ids) + { + using (var repository = _repositoryFactory.CreateMemberTypeRepository(_uowProvider.GetUnitOfWork())) + { + return repository.GetAll(ids); + } + } + + /// + /// Gets an object by its Id + /// + /// Id of the to retrieve + /// + public IMemberType GetMemberType(int id) + { + using (var repository = _repositoryFactory.CreateMemberTypeRepository(_uowProvider.GetUnitOfWork())) + { + return repository.Get(id); + } + } + + /// + /// Gets an object by its Alias + /// + /// Alias of the to retrieve + /// + public IMemberType GetMemberType(string alias) + { + using (var repository = _repositoryFactory.CreateMemberTypeRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.Alias == alias); + var contentTypes = repository.GetByQuery(query); + + return contentTypes.FirstOrDefault(); + } + } + + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/TagService.cs b/src/Umbraco.Core/Services/TagService.cs index 9abd731d85..179b2a6df7 100644 --- a/src/Umbraco.Core/Services/TagService.cs +++ b/src/Umbraco.Core/Services/TagService.cs @@ -1,127 +1,127 @@ -using System.Collections.Generic; -using Umbraco.Core.Models; -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.UnitOfWork; - -namespace Umbraco.Core.Services -{ - /// - /// Tag service to query for tags in the tags db table. The tags returned are only relavent for published content & saved media or members - /// - /// - /// If there is unpublished content with tags, those tags will not be contained - /// - public class TagService : ITagService - { - - private readonly RepositoryFactory _repositoryFactory; - private readonly IDatabaseUnitOfWorkProvider _uowProvider; - - public TagService() - : this(new RepositoryFactory()) - {} - - public TagService(RepositoryFactory repositoryFactory) - : this(new PetaPocoUnitOfWorkProvider(), repositoryFactory) - { - } - - public TagService(IDatabaseUnitOfWorkProvider provider) - : this(provider, new RepositoryFactory()) - { - } - - public TagService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory) - { - _repositoryFactory = repositoryFactory; - _uowProvider = provider; - } - - /// - /// Get every tag stored in the database (with optional group) - /// - public IEnumerable GetAllTags(string group = null) - { - using (var repository = _repositoryFactory.CreateTagsRepository(_uowProvider.GetUnitOfWork())) - { - if (group.IsNullOrWhiteSpace()) - { - return repository.GetAll(); - } - - var query = Query.Builder.Where(x => x.Group == group); - var definitions = repository.GetByQuery(query); - return definitions; - } - } - - /// - /// Get all tags for content items (with optional group) - /// - /// - /// - public IEnumerable GetAllContentTags(string group = null) - { - using (var repository = _repositoryFactory.CreateTagsRepository(_uowProvider.GetUnitOfWork())) - { - return repository.GetTagsForEntityType(TaggableObjectTypes.Content, group); - } - } - - /// - /// Get all tags for media items (with optional group) - /// - /// - /// - public IEnumerable GetAllMediaTags(string group = null) - { - using (var repository = _repositoryFactory.CreateTagsRepository(_uowProvider.GetUnitOfWork())) - { - return repository.GetTagsForEntityType(TaggableObjectTypes.Media, group); - } - } - - /// - /// Get all tags for member items (with optional group) - /// - /// - /// - public IEnumerable GetAllMemberTags(string group = null) - { - using (var repository = _repositoryFactory.CreateTagsRepository(_uowProvider.GetUnitOfWork())) - { - return repository.GetTagsForEntityType(TaggableObjectTypes.Member, group); - } - } - - /// - /// Returns all tags attached to a property by entity id - /// - /// - /// - /// - /// - public IEnumerable GetTagsForProperty(int contentId, string propertyTypeAlias, string tagGroup = null) - { - using (var repository = _repositoryFactory.CreateTagsRepository(_uowProvider.GetUnitOfWork())) - { - return repository.GetTagsForProperty(contentId, propertyTypeAlias, tagGroup); - } - } - - /// - /// Returns all tags attached to an entity (content, media or member) by entity id - /// - /// - /// - /// - public IEnumerable GetTagsForEntity(int contentId, string tagGroup = null) - { - using (var repository = _repositoryFactory.CreateTagsRepository(_uowProvider.GetUnitOfWork())) - { - return repository.GetTagsForEntity(contentId, tagGroup); - } - } - } +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.UnitOfWork; + +namespace Umbraco.Core.Services +{ + /// + /// Tag service to query for tags in the tags db table. The tags returned are only relavent for published content & saved media or members + /// + /// + /// If there is unpublished content with tags, those tags will not be contained + /// + public class TagService : ITagService + { + + private readonly RepositoryFactory _repositoryFactory; + private readonly IDatabaseUnitOfWorkProvider _uowProvider; + + public TagService() + : this(new RepositoryFactory()) + {} + + public TagService(RepositoryFactory repositoryFactory) + : this(new PetaPocoUnitOfWorkProvider(), repositoryFactory) + { + } + + public TagService(IDatabaseUnitOfWorkProvider provider) + : this(provider, new RepositoryFactory()) + { + } + + public TagService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory) + { + _repositoryFactory = repositoryFactory; + _uowProvider = provider; + } + + /// + /// Get every tag stored in the database (with optional group) + /// + public IEnumerable GetAllTags(string group = null) + { + using (var repository = _repositoryFactory.CreateTagsRepository(_uowProvider.GetUnitOfWork())) + { + if (group.IsNullOrWhiteSpace()) + { + return repository.GetAll(); + } + + var query = Query.Builder.Where(x => x.Group == group); + var definitions = repository.GetByQuery(query); + return definitions; + } + } + + /// + /// Get all tags for content items (with optional group) + /// + /// + /// + public IEnumerable GetAllContentTags(string group = null) + { + using (var repository = _repositoryFactory.CreateTagsRepository(_uowProvider.GetUnitOfWork())) + { + return repository.GetTagsForEntityType(TaggableObjectTypes.Content, group); + } + } + + /// + /// Get all tags for media items (with optional group) + /// + /// + /// + public IEnumerable GetAllMediaTags(string group = null) + { + using (var repository = _repositoryFactory.CreateTagsRepository(_uowProvider.GetUnitOfWork())) + { + return repository.GetTagsForEntityType(TaggableObjectTypes.Media, group); + } + } + + /// + /// Get all tags for member items (with optional group) + /// + /// + /// + public IEnumerable GetAllMemberTags(string group = null) + { + using (var repository = _repositoryFactory.CreateTagsRepository(_uowProvider.GetUnitOfWork())) + { + return repository.GetTagsForEntityType(TaggableObjectTypes.Member, group); + } + } + + /// + /// Returns all tags attached to a property by entity id + /// + /// + /// + /// + /// + public IEnumerable GetTagsForProperty(int contentId, string propertyTypeAlias, string tagGroup = null) + { + using (var repository = _repositoryFactory.CreateTagsRepository(_uowProvider.GetUnitOfWork())) + { + return repository.GetTagsForProperty(contentId, propertyTypeAlias, tagGroup); + } + } + + /// + /// Returns all tags attached to an entity (content, media or member) by entity id + /// + /// + /// + /// + public IEnumerable GetTagsForEntity(int contentId, string tagGroup = null) + { + using (var repository = _repositoryFactory.CreateTagsRepository(_uowProvider.GetUnitOfWork())) + { + return repository.GetTagsForEntity(contentId, tagGroup); + } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Xml/XPath/INavigableContent.cs b/src/Umbraco.Core/Xml/XPath/INavigableContent.cs index d8b6adf00a..d74e08c0be 100644 --- a/src/Umbraco.Core/Xml/XPath/INavigableContent.cs +++ b/src/Umbraco.Core/Xml/XPath/INavigableContent.cs @@ -1,59 +1,59 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Xml.XPath -{ - /// - /// Represents a content that can be navigated via XPath. - /// - interface INavigableContent - { - /// - /// Gets the unique identifier of the navigable content. - /// - /// The root node identifier should be -1. - int Id { get; } - - /// - /// Gets the unique identifier of parent of the navigable content. - /// - /// The top-level content parent identifiers should be -1 ie the identifier - /// of the root node, whose parent identifier should in turn be -1. - int ParentId { get; } - - /// - /// Gets the type of the navigable content. - /// - INavigableContentType Type { get; } - - /// - /// Gets the unique identifiers of the children of the navigable content. - /// - IList ChildIds { get; } - - /// - /// Gets the value of a field of the navigable content for XPath navigation use. - /// - /// The field index. - /// The value of the field for XPath navigation use. - /// - /// Fields are attributes or elements depending on their relative index value compared - /// to source.LastAttributeIndex. - /// For attributes, the value must be a string. - /// For elements, the value should an XPathNavigator instance if the field is xml - /// and has content (is not empty), null to indicate that the element is empty, or a string - /// which can be empty, whitespace... depending on what the data type wants to expose. - /// - object Value(int index); - - // TODO implement the following one - - ///// - ///// Gets the value of a field of the navigable content, for a specified language. - ///// - ///// The field index. - ///// The language key. - ///// The value of the field for the specified language. - ///// ... - //object Value(int index, string languageKey); - } -} +using System.Collections.Generic; + +namespace Umbraco.Core.Xml.XPath +{ + /// + /// Represents a content that can be navigated via XPath. + /// + interface INavigableContent + { + /// + /// Gets the unique identifier of the navigable content. + /// + /// The root node identifier should be -1. + int Id { get; } + + /// + /// Gets the unique identifier of parent of the navigable content. + /// + /// The top-level content parent identifiers should be -1 ie the identifier + /// of the root node, whose parent identifier should in turn be -1. + int ParentId { get; } + + /// + /// Gets the type of the navigable content. + /// + INavigableContentType Type { get; } + + /// + /// Gets the unique identifiers of the children of the navigable content. + /// + IList ChildIds { get; } + + /// + /// Gets the value of a field of the navigable content for XPath navigation use. + /// + /// The field index. + /// The value of the field for XPath navigation use. + /// + /// Fields are attributes or elements depending on their relative index value compared + /// to source.LastAttributeIndex. + /// For attributes, the value must be a string. + /// For elements, the value should an XPathNavigator instance if the field is xml + /// and has content (is not empty), null to indicate that the element is empty, or a string + /// which can be empty, whitespace... depending on what the data type wants to expose. + /// + object Value(int index); + + // TODO implement the following one + + ///// + ///// Gets the value of a field of the navigable content, for a specified language. + ///// + ///// The field index. + ///// The language key. + ///// The value of the field for the specified language. + ///// ... + //object Value(int index, string languageKey); + } +} diff --git a/src/Umbraco.Core/Xml/XPath/INavigableContentType.cs b/src/Umbraco.Core/Xml/XPath/INavigableContentType.cs index 94b225467c..bffeceb604 100644 --- a/src/Umbraco.Core/Xml/XPath/INavigableContentType.cs +++ b/src/Umbraco.Core/Xml/XPath/INavigableContentType.cs @@ -1,24 +1,24 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Umbraco.Core.Xml.XPath -{ - /// - /// Represents the type of a content that can be navigated via XPath. - /// - interface INavigableContentType - { - /// - /// Gets the name of the content type. - /// - string Name { get; } - - /// - /// Gets the field types of the content type. - /// - /// This includes the attributes and the properties. - INavigableFieldType[] FieldTypes { get; } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Umbraco.Core.Xml.XPath +{ + /// + /// Represents the type of a content that can be navigated via XPath. + /// + interface INavigableContentType + { + /// + /// Gets the name of the content type. + /// + string Name { get; } + + /// + /// Gets the field types of the content type. + /// + /// This includes the attributes and the properties. + INavigableFieldType[] FieldTypes { get; } + } +} diff --git a/src/Umbraco.Core/Xml/XPath/INavigableFieldType.cs b/src/Umbraco.Core/Xml/XPath/INavigableFieldType.cs index 32a6f64751..d2275fffe1 100644 --- a/src/Umbraco.Core/Xml/XPath/INavigableFieldType.cs +++ b/src/Umbraco.Core/Xml/XPath/INavigableFieldType.cs @@ -1,26 +1,26 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Umbraco.Core.Xml.XPath -{ - /// - /// Represents the type of a field of a content that can be navigated via XPath. - /// - /// A field can be an attribute or a property. - interface INavigableFieldType - { - /// - /// Gets the name of the field type. - /// - string Name { get; } - - /// - /// Gets a method to convert the field value to a string. - /// - /// This is for built-in properties, ie attributes. User-defined properties have their - /// own way to convert their value for XPath. - Func XmlStringConverter { get; } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Umbraco.Core.Xml.XPath +{ + /// + /// Represents the type of a field of a content that can be navigated via XPath. + /// + /// A field can be an attribute or a property. + interface INavigableFieldType + { + /// + /// Gets the name of the field type. + /// + string Name { get; } + + /// + /// Gets a method to convert the field value to a string. + /// + /// This is for built-in properties, ie attributes. User-defined properties have their + /// own way to convert their value for XPath. + Func XmlStringConverter { get; } + } +} diff --git a/src/Umbraco.Core/Xml/XPath/INavigableSource.cs b/src/Umbraco.Core/Xml/XPath/INavigableSource.cs index 68dc9e906a..977461076e 100644 --- a/src/Umbraco.Core/Xml/XPath/INavigableSource.cs +++ b/src/Umbraco.Core/Xml/XPath/INavigableSource.cs @@ -1,34 +1,34 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Umbraco.Core.Xml.XPath -{ - /// - /// Represents a source of content that can be navigated via XPath. - /// - interface INavigableSource - { - /// - /// Gets a content identified by its unique identifier. - /// - /// The unique identifier. - /// The content identified by the unique identifier, or null. - /// When id is -1 (root content) implementations should return null. - INavigableContent Get(int id); - - /// - /// Gets the index of the last attribute in the fields collections. - /// - int LastAttributeIndex { get; } - - /// - /// Gets the content at the root of the source. - /// - /// That content should have unique identifier -1 and should not be gettable, - /// ie Get(-1) should return null. Its ParentId should be -1. It should provide - /// values for the attribute fields. - INavigableContent Root { get; } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Umbraco.Core.Xml.XPath +{ + /// + /// Represents a source of content that can be navigated via XPath. + /// + interface INavigableSource + { + /// + /// Gets a content identified by its unique identifier. + /// + /// The unique identifier. + /// The content identified by the unique identifier, or null. + /// When id is -1 (root content) implementations should return null. + INavigableContent Get(int id); + + /// + /// Gets the index of the last attribute in the fields collections. + /// + int LastAttributeIndex { get; } + + /// + /// Gets the content at the root of the source. + /// + /// That content should have unique identifier -1 and should not be gettable, + /// ie Get(-1) should return null. Its ParentId should be -1. It should provide + /// values for the attribute fields. + INavigableContent Root { get; } + } +} diff --git a/src/Umbraco.Core/Xml/XPath/MacroNavigator.cs b/src/Umbraco.Core/Xml/XPath/MacroNavigator.cs index 501c8c3d1a..b44b7d5f6e 100644 --- a/src/Umbraco.Core/Xml/XPath/MacroNavigator.cs +++ b/src/Umbraco.Core/Xml/XPath/MacroNavigator.cs @@ -1,1042 +1,1042 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Xml; -using System.Xml.XPath; - -namespace Umbraco.Core.Xml.XPath -{ - /// - /// Provides a cursor model for navigating {macro /} as if it were XML. - /// - class MacroNavigator : XPathNavigator - { - private readonly XmlNameTable _nameTable; - private readonly MacroRoot _macro; - private State _state; - - #region Constructor - - /// - /// Initializes a new instance of the class with macro parameters. - /// - /// The macro parameters. - public MacroNavigator(IEnumerable parameters) - : this(new MacroRoot(parameters), new NameTable(), new State()) - { } - - /// - /// Initializes a new instance of the class with a macro node, - /// a name table and a state. - /// - /// The macro node. - /// The name table. - /// The state. - /// Privately used for cloning a navigator. - private MacroNavigator(MacroRoot macro, XmlNameTable nameTable, State state) - { - _macro = macro; - _nameTable = nameTable; - _state = state; - } - - #endregion - - #region Diagnostics - - // diagnostics code will not be compiled nor called into Release configuration. - // in Debug configuration, uncomment lines in Debug() to write to console or to log. - // - // much of this code is duplicated in each navigator due to conditional compilation - -#if DEBUG - private const string Tabs = " "; - private int _tabs; - private readonly int _uid = GetUid(); - private static int _uidg; - private readonly static object Uidl = new object(); - private static int GetUid() - { - lock (Uidl) - { - return _uidg++; - } - } -#endif - - [Conditional("DEBUG")] - void DebugEnter(string name) - { -#if DEBUG - Debug(""); - DebugState(":"); - Debug(name); - _tabs = Math.Min(Tabs.Length, _tabs + 2); -#endif - } - - [Conditional("DEBUG")] - void DebugCreate(MacroNavigator nav) - { -#if DEBUG - Debug("Create: [MacroNavigator::{0}]", nav._uid); -#endif - } - - [Conditional("DEBUG")] - private void DebugReturn() - { -#if DEBUG -// ReSharper disable IntroduceOptionalParameters.Local - DebugReturn("(void)"); -// ReSharper restore IntroduceOptionalParameters.Local -#endif - } - - [Conditional("DEBUG")] - private void DebugReturn(bool value) - { -#if DEBUG - DebugReturn(value ? "true" : "false"); -#endif - } - - [Conditional("DEBUG")] - void DebugReturn(string format, params object[] args) - { -#if DEBUG - Debug("=> " + format, args); - if (_tabs > 0) _tabs -= 2; -#endif - } - - [Conditional("DEBUG")] - void DebugState(string s = " =>") - { -#if DEBUG - string position; - - switch (_state.Position) - { - case StatePosition.Macro: - position = "At macro."; - break; - case StatePosition.Parameter: - position = string.Format("At parameter '{0}'.", - _macro.Parameters[_state.ParameterIndex].Name); - break; - case StatePosition.ParameterAttribute: - position = string.Format("At parameter attribute '{0}/{1}'.", - _macro.Parameters[_state.ParameterIndex].Name, - _macro.Parameters[_state.ParameterIndex].Attributes[_state.ParameterAttributeIndex].Key); - break; - case StatePosition.ParameterNavigator: - position = string.Format("In parameter '{0}{1}' navigator.", - _macro.Parameters[_state.ParameterIndex].Name, - _macro.Parameters[_state.ParameterIndex].WrapNavigatorInNodes ? "/nodes" : ""); - break; - case StatePosition.ParameterNodes: - position = string.Format("At parameter '{0}/nodes'.", - _macro.Parameters[_state.ParameterIndex].Name); - break; - case StatePosition.ParameterText: - position = string.Format("In parameter '{0}' text.", - _macro.Parameters[_state.ParameterIndex].Name); - break; - case StatePosition.Root: - position = "At root."; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - Debug("State{0} {1}", s, position); -#endif - } - -#if DEBUG - void Debug(string format, params object[] args) - { - // remove comments to write - - format = "[" + _uid.ToString("00000") + "] " + Tabs.Substring(0, _tabs) + format; -#pragma warning disable 168 - var msg = string.Format(format, args); // unused if not writing, hence #pragma -#pragma warning restore 168 - //LogHelper.Debug(msg); // beware! this can quicky overflow log4net - //Console.WriteLine(msg); - } -#endif - - #endregion - - #region Macro - - private class MacroRoot - { - public MacroRoot(IEnumerable parameters) - { - Parameters = parameters == null ? new MacroParameter[] {} : parameters.ToArray(); - } - - public MacroParameter[] Parameters { get; private set; } - } - - public class MacroParameter - { - // note: assuming we're not thinking about supporting - // XPathIterator in parameters - enough nonsense! - - public MacroParameter(string name, string value) - { - Name = name; - StringValue = value; - } - - public MacroParameter(string name, XPathNavigator navigator, - int maxNavigatorDepth = int.MaxValue, - bool wrapNavigatorInNodes = false, - IEnumerable> attributes = null) - { - Name = name; - MaxNavigatorDepth = maxNavigatorDepth; - WrapNavigatorInNodes = wrapNavigatorInNodes; - if (attributes != null) - { - var a = attributes.ToArray(); - if (a.Length > 0) - Attributes = a; - } - NavigatorValue = navigator; // should not be empty - } - - public string Name { get; private set; } - public string StringValue { get; private set; } - public XPathNavigator NavigatorValue { get; private set; } - public int MaxNavigatorDepth { get; private set; } - public bool WrapNavigatorInNodes { get; private set; } - public KeyValuePair[] Attributes { get; private set; } - } - - #endregion - - /// - /// Creates a new XPathNavigator positioned at the same node as this XPathNavigator. - /// - /// A new XPathNavigator positioned at the same node as this XPathNavigator. - public override XPathNavigator Clone() - { - DebugEnter("Clone"); - var nav = new MacroNavigator(_macro, _nameTable, _state.Clone()); - DebugCreate(nav); - DebugReturn("[XPathNavigator]"); - return nav; - } - - /// - /// Gets a value indicating whether the current node is an empty element without an end element tag. - /// - public override bool IsEmptyElement - { - get - { - DebugEnter("IsEmptyElement"); - bool isEmpty; - - switch (_state.Position) - { - case StatePosition.Macro: - isEmpty = _macro.Parameters.Length == 0; - break; - case StatePosition.Parameter: - var parameter = _macro.Parameters[_state.ParameterIndex]; - var nav = parameter.NavigatorValue; - if (parameter.WrapNavigatorInNodes || nav != null) - { - isEmpty = false; - } - else - { - var s = _macro.Parameters[_state.ParameterIndex].StringValue; - isEmpty = s == null; - } - break; - case StatePosition.ParameterNavigator: - isEmpty = _state.ParameterNavigator.IsEmptyElement; - break; - case StatePosition.ParameterNodes: - isEmpty = _macro.Parameters[_state.ParameterIndex].NavigatorValue == null; - break; - case StatePosition.ParameterAttribute: - case StatePosition.ParameterText: - case StatePosition.Root: - throw new InvalidOperationException("Not an element."); - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn(isEmpty); - return isEmpty; - } - } - - /// - /// Determines whether the current XPathNavigator is at the same position as the specified XPathNavigator. - /// - /// The XPathNavigator to compare to this XPathNavigator. - /// true if the two XPathNavigator objects have the same position; otherwise, false. - public override bool IsSamePosition(XPathNavigator nav) - { - DebugEnter("IsSamePosition"); - bool isSame; - - switch (_state.Position) - { - case StatePosition.ParameterNavigator: - case StatePosition.Macro: - case StatePosition.Parameter: - case StatePosition.ParameterAttribute: - case StatePosition.ParameterNodes: - case StatePosition.ParameterText: - case StatePosition.Root: - var other = nav as MacroNavigator; - isSame = other != null && other._macro == _macro && _state.IsSamePosition(other._state); - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn(isSame); - return isSame; - } - - /// - /// Gets the qualified name of the current node. - /// - public override string Name - { - get - { - DebugEnter("Name"); - string name; - - switch (_state.Position) - { - case StatePosition.Macro: - name = "macro"; - break; - case StatePosition.Parameter: - name = _macro.Parameters[_state.ParameterIndex].Name; - break; - case StatePosition.ParameterAttribute: - name = _macro.Parameters[_state.ParameterIndex].Attributes[_state.ParameterAttributeIndex].Key; - break; - case StatePosition.ParameterNavigator: - name = _state.ParameterNavigator.Name; - break; - case StatePosition.ParameterNodes: - name = "nodes"; - break; - case StatePosition.ParameterText: - case StatePosition.Root: - name = string.Empty; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn("\"{0}\"", name); - return name; - } - } - - /// - /// Gets the Name of the current node without any namespace prefix. - /// - public override string LocalName - { - get - { - DebugEnter("LocalName"); - var name = Name; - DebugReturn("\"{0}\"", name); - return name; - } - } - - /// - /// Moves the XPathNavigator to the same position as the specified XPathNavigator. - /// - /// The XPathNavigator positioned on the node that you want to move to. - /// Returns true if the XPathNavigator is successful moving to the same position as the specified XPathNavigator; - /// otherwise, false. If false, the position of the XPathNavigator is unchanged. - public override bool MoveTo(XPathNavigator nav) - { - DebugEnter("MoveTo"); - - var other = nav as MacroNavigator; - var succ = false; - - if (other != null && other._macro == _macro) - { - _state = other._state.Clone(); - DebugState(); - succ = true; - } - - DebugReturn(succ); - return succ; - } - - /// - /// Moves the XPathNavigator to the first attribute of the current node. - /// - /// Returns true if the XPathNavigator is successful moving to the first attribute of the current node; - /// otherwise, false. If false, the position of the XPathNavigator is unchanged. - public override bool MoveToFirstAttribute() - { - DebugEnter("MoveToFirstAttribute"); - bool succ; - - switch (_state.Position) - { - case StatePosition.ParameterNavigator: - succ = _state.ParameterNavigator.MoveToFirstAttribute(); - break; - case StatePosition.Parameter: - if (_macro.Parameters[_state.ParameterIndex].Attributes != null) - { - _state.Position = StatePosition.ParameterAttribute; - _state.ParameterAttributeIndex = 0; - succ = true; - DebugState(); - } - else succ = false; - break; - case StatePosition.ParameterAttribute: - case StatePosition.ParameterNodes: - case StatePosition.Macro: - case StatePosition.ParameterText: - case StatePosition.Root: - succ = false; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn(succ); - return succ; - } - - /// - /// Moves the XPathNavigator to the first child node of the current node. - /// - /// Returns true if the XPathNavigator is successful moving to the first child node of the current node; - /// otherwise, false. If false, the position of the XPathNavigator is unchanged. - public override bool MoveToFirstChild() - { - DebugEnter("MoveToFirstChild"); - bool succ; - - switch (_state.Position) - { - case StatePosition.Macro: - if (_macro.Parameters.Length == 0) - { - succ = false; - } - else - { - _state.ParameterIndex = 0; - _state.Position = StatePosition.Parameter; - succ = true; - } - break; - case StatePosition.Parameter: - var parameter = _macro.Parameters[_state.ParameterIndex]; - var nav = parameter.NavigatorValue; - if (parameter.WrapNavigatorInNodes) - { - _state.Position = StatePosition.ParameterNodes; - DebugState(); - succ = true; - } - else if (nav != null) - { - nav = nav.Clone(); // never use the raw parameter's navigator - nav.MoveToFirstChild(); - _state.ParameterNavigator = nav; - _state.ParameterNavigatorDepth = 0; - _state.Position = StatePosition.ParameterNavigator; - DebugState(); - succ = true; - } - else - { - var s = _macro.Parameters[_state.ParameterIndex].StringValue; - if (s != null) - { - _state.Position = StatePosition.ParameterText; - DebugState(); - succ = true; - } - else succ = false; - } - break; - case StatePosition.ParameterNavigator: - if (_state.ParameterNavigatorDepth == _macro.Parameters[_state.ParameterIndex].MaxNavigatorDepth) - { - succ = false; - } - else - { - // move to first doc child => increment depth, else (property child) do nothing - succ = _state.ParameterNavigator.MoveToFirstChild(); - if (succ && IsDoc(_state.ParameterNavigator)) - { - ++_state.ParameterNavigatorDepth; - DebugState(); - } - } - break; - case StatePosition.ParameterNodes: - if (_macro.Parameters[_state.ParameterIndex].NavigatorValue != null) - { - // never use the raw parameter's navigator - _state.ParameterNavigator = _macro.Parameters[_state.ParameterIndex].NavigatorValue.Clone(); - _state.Position = StatePosition.ParameterNavigator; - succ = true; - DebugState(); - } - else succ = false; - break; - case StatePosition.ParameterAttribute: - case StatePosition.ParameterText: - succ = false; - break; - case StatePosition.Root: - _state.Position = StatePosition.Macro; - DebugState(); - succ = true; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn(succ); - return succ; - } - - /// - /// Moves the XPathNavigator to the first namespace node that matches the XPathNamespaceScope specified. - /// - /// An XPathNamespaceScope value describing the namespace scope. - /// Returns true if the XPathNavigator is successful moving to the first namespace node; - /// otherwise, false. If false, the position of the XPathNavigator is unchanged. - public override bool MoveToFirstNamespace(XPathNamespaceScope namespaceScope) - { - DebugEnter("MoveToFirstNamespace"); - DebugReturn(false); - return false; - } - - /// - /// Moves the XPathNavigator to the next namespace node matching the XPathNamespaceScope specified. - /// - /// An XPathNamespaceScope value describing the namespace scope. - /// Returns true if the XPathNavigator is successful moving to the next namespace node; - /// otherwise, false. If false, the position of the XPathNavigator is unchanged. - public override bool MoveToNextNamespace(XPathNamespaceScope namespaceScope) - { - DebugEnter("MoveToNextNamespace"); - DebugReturn(false); - return false; - } - - /// - /// Moves to the node that has an attribute of type ID whose value matches the specified String. - /// - /// A String representing the ID value of the node to which you want to move. - /// true if the XPathNavigator is successful moving; otherwise, false. - /// If false, the position of the navigator is unchanged. - public override bool MoveToId(string id) - { - DebugEnter("MoveToId"); - // impossible to implement since parameters can contain duplicate fragments of the - // main xml and therefore there can be duplicate unique node identifiers. - DebugReturn("NotImplementedException"); - throw new NotImplementedException(); - } - - /// - /// Moves the XPathNavigator to the next sibling node of the current node. - /// - /// true if the XPathNavigator is successful moving to the next sibling node; - /// otherwise, false if there are no more siblings or if the XPathNavigator is currently - /// positioned on an attribute node. If false, the position of the XPathNavigator is unchanged. - public override bool MoveToNext() - { - DebugEnter("MoveToNext"); - bool succ; - - switch (_state.Position) - { - case StatePosition.Parameter: - if (_state.ParameterIndex == _macro.Parameters.Length - 1) - { - succ = false; - } - else - { - ++_state.ParameterIndex; - DebugState(); - succ = true; - } - break; - case StatePosition.ParameterNavigator: - var wasDoc = IsDoc(_state.ParameterNavigator); - succ = _state.ParameterNavigator.MoveToNext(); - if (succ && !wasDoc && IsDoc(_state.ParameterNavigator)) - { - // move to first doc child => increment depth, else (another property child) do nothing - if (_state.ParameterNavigatorDepth == _macro.Parameters[_state.ParameterIndex].MaxNavigatorDepth) - { - _state.ParameterNavigator.MoveToPrevious(); - succ = false; - } - else - { - ++_state.ParameterNavigatorDepth; - DebugState(); - } - } - break; - case StatePosition.ParameterNodes: - case StatePosition.ParameterAttribute: - case StatePosition.ParameterText: - case StatePosition.Macro: - case StatePosition.Root: - succ = false; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn(succ); - return succ; - } - - /// - /// Moves the XPathNavigator to the previous sibling node of the current node. - /// - /// Returns true if the XPathNavigator is successful moving to the previous sibling node; - /// otherwise, false if there is no previous sibling node or if the XPathNavigator is currently - /// positioned on an attribute node. If false, the position of the XPathNavigator is unchanged. - public override bool MoveToPrevious() - { - DebugEnter("MoveToPrevious"); - bool succ; - - switch (_state.Position) - { - case StatePosition.Parameter: - if (_state.ParameterIndex == -1) - { - succ = false; - } - else - { - --_state.ParameterIndex; - DebugState(); - succ = true; - } - break; - case StatePosition.ParameterNavigator: - var wasDoc = IsDoc(_state.ParameterNavigator); - succ = _state.ParameterNavigator.MoveToPrevious(); - if (succ && wasDoc && !IsDoc(_state.ParameterNavigator)) - { - // move from doc child back to property child => decrement depth - --_state.ParameterNavigatorDepth; - DebugState(); - } - break; - case StatePosition.ParameterAttribute: - case StatePosition.ParameterNodes: - case StatePosition.ParameterText: - case StatePosition.Macro: - case StatePosition.Root: - succ = false; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn(succ); - return succ; - } - - /// - /// Moves the XPathNavigator to the next attribute. - /// - /// Returns true if the XPathNavigator is successful moving to the next attribute; - /// false if there are no more attributes. If false, the position of the XPathNavigator is unchanged. - public override bool MoveToNextAttribute() - { - DebugEnter("MoveToNextAttribute"); - bool succ; - - switch (_state.Position) - { - case StatePosition.ParameterNavigator: - succ = _state.ParameterNavigator.MoveToNextAttribute(); - break; - case StatePosition.ParameterAttribute: - if (_state.ParameterAttributeIndex == _macro.Parameters[_state.ParameterIndex].Attributes.Length - 1) - succ = false; - else - { - ++_state.ParameterAttributeIndex; - DebugState(); - succ = true; - } - break; - case StatePosition.Parameter: - case StatePosition.ParameterNodes: - case StatePosition.ParameterText: - case StatePosition.Macro: - case StatePosition.Root: - succ = false; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn(succ); - return succ; - } - - /// - /// Moves the XPathNavigator to the parent node of the current node. - /// - /// Returns true if the XPathNavigator is successful moving to the parent node of the current node; - /// otherwise, false. If false, the position of the XPathNavigator is unchanged. - public override bool MoveToParent() - { - DebugEnter("MoveToParent"); - bool succ; - - switch (_state.Position) - { - case StatePosition.Macro: - _state.Position = StatePosition.Root; - DebugState(); - succ = true; - break; - case StatePosition.Parameter: - _state.Position = StatePosition.Macro; - DebugState(); - succ = true; - break; - case StatePosition.ParameterAttribute: - case StatePosition.ParameterNodes: - _state.Position = StatePosition.Parameter; - DebugState(); - succ = true; - break; - case StatePosition.ParameterNavigator: - var wasDoc = IsDoc(_state.ParameterNavigator); - succ = _state.ParameterNavigator.MoveToParent(); - if (succ) - { - // move from doc child => decrement depth - if (wasDoc && --_state.ParameterNavigatorDepth == 0) - { - _state.Position = StatePosition.Parameter; - _state.ParameterNavigator = null; - DebugState(); - } - } - break; - case StatePosition.ParameterText: - _state.Position = StatePosition.Parameter; - DebugState(); - succ = true; - break; - case StatePosition.Root: - succ = false; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn(succ); - return succ; - } - - /// - /// Moves the XPathNavigator to the root node that the current node belongs to. - /// - public override void MoveToRoot() - { - DebugEnter("MoveToRoot"); - - switch (_state.Position) - { - case StatePosition.ParameterNavigator: - _state.ParameterNavigator = null; - _state.ParameterNavigatorDepth = -1; - break; - case StatePosition.Parameter: - case StatePosition.ParameterText: - _state.ParameterIndex = -1; - break; - case StatePosition.ParameterAttribute: - case StatePosition.ParameterNodes: - case StatePosition.Macro: - case StatePosition.Root: - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - _state.Position = StatePosition.Root; - DebugState(); - - DebugReturn(); - } - - /// - /// Gets the base URI for the current node. - /// - public override string BaseURI - { - get { return string.Empty; } - } - - /// - /// Gets the XmlNameTable of the XPathNavigator. - /// - public override XmlNameTable NameTable - { - get { return _nameTable; } - } - - /// - /// Gets the namespace URI of the current node. - /// - public override string NamespaceURI - { - get { return string.Empty; } - } - - /// - /// Gets the XPathNodeType of the current node. - /// - public override XPathNodeType NodeType - { - get - { - DebugEnter("NodeType"); - XPathNodeType type; - - switch (_state.Position) - { - case StatePosition.Macro: - case StatePosition.Parameter: - case StatePosition.ParameterNodes: - type = XPathNodeType.Element; - break; - case StatePosition.ParameterNavigator: - type = _state.ParameterNavigator.NodeType; - break; - case StatePosition.ParameterAttribute: - type = XPathNodeType.Attribute; - break; - case StatePosition.ParameterText: - type = XPathNodeType.Text; - break; - case StatePosition.Root: - type = XPathNodeType.Root; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn("\'{0}\'", type); - return type; - } - } - - /// - /// Gets the namespace prefix associated with the current node. - /// - public override string Prefix - { - get { return string.Empty; } - } - - /// - /// Gets the string value of the item. - /// - /// Does not fully behave as per the specs, as we report empty value on root and macro elements, and we start - /// reporting values only on parameter elements. This is because, otherwise, we would might dump the whole database - /// and it probably does not make sense at Umbraco level. - public override string Value - { - get - { - DebugEnter("Value"); - string value; - - XPathNavigator nav; - switch (_state.Position) - { - case StatePosition.Parameter: - nav = _macro.Parameters[_state.ParameterIndex].NavigatorValue; - if (nav != null) - { - nav = nav.Clone(); // never use the raw parameter's navigator - nav.MoveToFirstChild(); - value = nav.Value; - } - else - { - var s = _macro.Parameters[_state.ParameterIndex].StringValue; - value = s ?? string.Empty; - } - break; - case StatePosition.ParameterAttribute: - value = _macro.Parameters[_state.ParameterIndex].Attributes[_state.ParameterAttributeIndex].Value; - break; - case StatePosition.ParameterNavigator: - value = _state.ParameterNavigator.Value; - break; - case StatePosition.ParameterNodes: - nav = _macro.Parameters[_state.ParameterIndex].NavigatorValue; - if (nav == null) - value = string.Empty; - else - { - nav = nav.Clone(); // never use the raw parameter's navigator - nav.MoveToFirstChild(); - value = nav.Value; - } - break; - case StatePosition.ParameterText: - value = _macro.Parameters[_state.ParameterIndex].StringValue; - break; - case StatePosition.Macro: - case StatePosition.Root: - value = string.Empty; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn("\"{0}\"", value); - return value; - } - } - - private static bool IsDoc(XPathNavigator nav) - { - if (nav.NodeType != XPathNodeType.Element) - return false; - - var clone = nav.Clone(); - if (!clone.MoveToFirstAttribute()) - return false; - do - { - if (clone.Name == "isDoc") - return true; - } while (clone.MoveToNextAttribute()); - - return false; - } - - #region State management - - // the possible state positions - internal enum StatePosition - { - Root, - Macro, - Parameter, - ParameterAttribute, - ParameterText, - ParameterNodes, - ParameterNavigator - }; - - // gets the state - // for unit tests only - internal State InternalState { get { return _state; } } - - // represents the XPathNavigator state - internal class State - { - public StatePosition Position { get; set; } - - // initialize a new state - private State(StatePosition position) - { - Position = position; - ParameterIndex = 0; - ParameterNavigatorDepth = 0; - ParameterAttributeIndex = 0; - } - - // initialize a new state - // used for creating the very first state - public State() - : this(StatePosition.Root) - { } - - // initialize a clone state - private State(State other) - { - Position = other.Position; - - ParameterIndex = other.ParameterIndex; - - if (Position == StatePosition.ParameterNavigator) - { - ParameterNavigator = other.ParameterNavigator.Clone(); - ParameterNavigatorDepth = other.ParameterNavigatorDepth; - ParameterAttributeIndex = other.ParameterAttributeIndex; - } - } - - public State Clone() - { - return new State(this); - } - - // the index of the current element - public int ParameterIndex { get; set; } - - // the current depth within the element navigator - public int ParameterNavigatorDepth { get; set; } - - // the index of the current element's attribute - public int ParameterAttributeIndex { get; set; } - - // gets or sets the element navigator - public XPathNavigator ParameterNavigator { get; set; } - - // gets a value indicating whether this state is at the same position as another one. - public bool IsSamePosition(State other) - { - return other.Position == Position - && (Position != StatePosition.ParameterNavigator || other.ParameterNavigator.IsSamePosition(ParameterNavigator)) - && other.ParameterIndex == ParameterIndex - && other.ParameterAttributeIndex == ParameterAttributeIndex; - } - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Xml; +using System.Xml.XPath; + +namespace Umbraco.Core.Xml.XPath +{ + /// + /// Provides a cursor model for navigating {macro /} as if it were XML. + /// + class MacroNavigator : XPathNavigator + { + private readonly XmlNameTable _nameTable; + private readonly MacroRoot _macro; + private State _state; + + #region Constructor + + /// + /// Initializes a new instance of the class with macro parameters. + /// + /// The macro parameters. + public MacroNavigator(IEnumerable parameters) + : this(new MacroRoot(parameters), new NameTable(), new State()) + { } + + /// + /// Initializes a new instance of the class with a macro node, + /// a name table and a state. + /// + /// The macro node. + /// The name table. + /// The state. + /// Privately used for cloning a navigator. + private MacroNavigator(MacroRoot macro, XmlNameTable nameTable, State state) + { + _macro = macro; + _nameTable = nameTable; + _state = state; + } + + #endregion + + #region Diagnostics + + // diagnostics code will not be compiled nor called into Release configuration. + // in Debug configuration, uncomment lines in Debug() to write to console or to log. + // + // much of this code is duplicated in each navigator due to conditional compilation + +#if DEBUG + private const string Tabs = " "; + private int _tabs; + private readonly int _uid = GetUid(); + private static int _uidg; + private readonly static object Uidl = new object(); + private static int GetUid() + { + lock (Uidl) + { + return _uidg++; + } + } +#endif + + [Conditional("DEBUG")] + void DebugEnter(string name) + { +#if DEBUG + Debug(""); + DebugState(":"); + Debug(name); + _tabs = Math.Min(Tabs.Length, _tabs + 2); +#endif + } + + [Conditional("DEBUG")] + void DebugCreate(MacroNavigator nav) + { +#if DEBUG + Debug("Create: [MacroNavigator::{0}]", nav._uid); +#endif + } + + [Conditional("DEBUG")] + private void DebugReturn() + { +#if DEBUG +// ReSharper disable IntroduceOptionalParameters.Local + DebugReturn("(void)"); +// ReSharper restore IntroduceOptionalParameters.Local +#endif + } + + [Conditional("DEBUG")] + private void DebugReturn(bool value) + { +#if DEBUG + DebugReturn(value ? "true" : "false"); +#endif + } + + [Conditional("DEBUG")] + void DebugReturn(string format, params object[] args) + { +#if DEBUG + Debug("=> " + format, args); + if (_tabs > 0) _tabs -= 2; +#endif + } + + [Conditional("DEBUG")] + void DebugState(string s = " =>") + { +#if DEBUG + string position; + + switch (_state.Position) + { + case StatePosition.Macro: + position = "At macro."; + break; + case StatePosition.Parameter: + position = string.Format("At parameter '{0}'.", + _macro.Parameters[_state.ParameterIndex].Name); + break; + case StatePosition.ParameterAttribute: + position = string.Format("At parameter attribute '{0}/{1}'.", + _macro.Parameters[_state.ParameterIndex].Name, + _macro.Parameters[_state.ParameterIndex].Attributes[_state.ParameterAttributeIndex].Key); + break; + case StatePosition.ParameterNavigator: + position = string.Format("In parameter '{0}{1}' navigator.", + _macro.Parameters[_state.ParameterIndex].Name, + _macro.Parameters[_state.ParameterIndex].WrapNavigatorInNodes ? "/nodes" : ""); + break; + case StatePosition.ParameterNodes: + position = string.Format("At parameter '{0}/nodes'.", + _macro.Parameters[_state.ParameterIndex].Name); + break; + case StatePosition.ParameterText: + position = string.Format("In parameter '{0}' text.", + _macro.Parameters[_state.ParameterIndex].Name); + break; + case StatePosition.Root: + position = "At root."; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + Debug("State{0} {1}", s, position); +#endif + } + +#if DEBUG + void Debug(string format, params object[] args) + { + // remove comments to write + + format = "[" + _uid.ToString("00000") + "] " + Tabs.Substring(0, _tabs) + format; +#pragma warning disable 168 + var msg = string.Format(format, args); // unused if not writing, hence #pragma +#pragma warning restore 168 + //LogHelper.Debug(msg); // beware! this can quicky overflow log4net + //Console.WriteLine(msg); + } +#endif + + #endregion + + #region Macro + + private class MacroRoot + { + public MacroRoot(IEnumerable parameters) + { + Parameters = parameters == null ? new MacroParameter[] {} : parameters.ToArray(); + } + + public MacroParameter[] Parameters { get; private set; } + } + + public class MacroParameter + { + // note: assuming we're not thinking about supporting + // XPathIterator in parameters - enough nonsense! + + public MacroParameter(string name, string value) + { + Name = name; + StringValue = value; + } + + public MacroParameter(string name, XPathNavigator navigator, + int maxNavigatorDepth = int.MaxValue, + bool wrapNavigatorInNodes = false, + IEnumerable> attributes = null) + { + Name = name; + MaxNavigatorDepth = maxNavigatorDepth; + WrapNavigatorInNodes = wrapNavigatorInNodes; + if (attributes != null) + { + var a = attributes.ToArray(); + if (a.Length > 0) + Attributes = a; + } + NavigatorValue = navigator; // should not be empty + } + + public string Name { get; private set; } + public string StringValue { get; private set; } + public XPathNavigator NavigatorValue { get; private set; } + public int MaxNavigatorDepth { get; private set; } + public bool WrapNavigatorInNodes { get; private set; } + public KeyValuePair[] Attributes { get; private set; } + } + + #endregion + + /// + /// Creates a new XPathNavigator positioned at the same node as this XPathNavigator. + /// + /// A new XPathNavigator positioned at the same node as this XPathNavigator. + public override XPathNavigator Clone() + { + DebugEnter("Clone"); + var nav = new MacroNavigator(_macro, _nameTable, _state.Clone()); + DebugCreate(nav); + DebugReturn("[XPathNavigator]"); + return nav; + } + + /// + /// Gets a value indicating whether the current node is an empty element without an end element tag. + /// + public override bool IsEmptyElement + { + get + { + DebugEnter("IsEmptyElement"); + bool isEmpty; + + switch (_state.Position) + { + case StatePosition.Macro: + isEmpty = _macro.Parameters.Length == 0; + break; + case StatePosition.Parameter: + var parameter = _macro.Parameters[_state.ParameterIndex]; + var nav = parameter.NavigatorValue; + if (parameter.WrapNavigatorInNodes || nav != null) + { + isEmpty = false; + } + else + { + var s = _macro.Parameters[_state.ParameterIndex].StringValue; + isEmpty = s == null; + } + break; + case StatePosition.ParameterNavigator: + isEmpty = _state.ParameterNavigator.IsEmptyElement; + break; + case StatePosition.ParameterNodes: + isEmpty = _macro.Parameters[_state.ParameterIndex].NavigatorValue == null; + break; + case StatePosition.ParameterAttribute: + case StatePosition.ParameterText: + case StatePosition.Root: + throw new InvalidOperationException("Not an element."); + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn(isEmpty); + return isEmpty; + } + } + + /// + /// Determines whether the current XPathNavigator is at the same position as the specified XPathNavigator. + /// + /// The XPathNavigator to compare to this XPathNavigator. + /// true if the two XPathNavigator objects have the same position; otherwise, false. + public override bool IsSamePosition(XPathNavigator nav) + { + DebugEnter("IsSamePosition"); + bool isSame; + + switch (_state.Position) + { + case StatePosition.ParameterNavigator: + case StatePosition.Macro: + case StatePosition.Parameter: + case StatePosition.ParameterAttribute: + case StatePosition.ParameterNodes: + case StatePosition.ParameterText: + case StatePosition.Root: + var other = nav as MacroNavigator; + isSame = other != null && other._macro == _macro && _state.IsSamePosition(other._state); + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn(isSame); + return isSame; + } + + /// + /// Gets the qualified name of the current node. + /// + public override string Name + { + get + { + DebugEnter("Name"); + string name; + + switch (_state.Position) + { + case StatePosition.Macro: + name = "macro"; + break; + case StatePosition.Parameter: + name = _macro.Parameters[_state.ParameterIndex].Name; + break; + case StatePosition.ParameterAttribute: + name = _macro.Parameters[_state.ParameterIndex].Attributes[_state.ParameterAttributeIndex].Key; + break; + case StatePosition.ParameterNavigator: + name = _state.ParameterNavigator.Name; + break; + case StatePosition.ParameterNodes: + name = "nodes"; + break; + case StatePosition.ParameterText: + case StatePosition.Root: + name = string.Empty; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn("\"{0}\"", name); + return name; + } + } + + /// + /// Gets the Name of the current node without any namespace prefix. + /// + public override string LocalName + { + get + { + DebugEnter("LocalName"); + var name = Name; + DebugReturn("\"{0}\"", name); + return name; + } + } + + /// + /// Moves the XPathNavigator to the same position as the specified XPathNavigator. + /// + /// The XPathNavigator positioned on the node that you want to move to. + /// Returns true if the XPathNavigator is successful moving to the same position as the specified XPathNavigator; + /// otherwise, false. If false, the position of the XPathNavigator is unchanged. + public override bool MoveTo(XPathNavigator nav) + { + DebugEnter("MoveTo"); + + var other = nav as MacroNavigator; + var succ = false; + + if (other != null && other._macro == _macro) + { + _state = other._state.Clone(); + DebugState(); + succ = true; + } + + DebugReturn(succ); + return succ; + } + + /// + /// Moves the XPathNavigator to the first attribute of the current node. + /// + /// Returns true if the XPathNavigator is successful moving to the first attribute of the current node; + /// otherwise, false. If false, the position of the XPathNavigator is unchanged. + public override bool MoveToFirstAttribute() + { + DebugEnter("MoveToFirstAttribute"); + bool succ; + + switch (_state.Position) + { + case StatePosition.ParameterNavigator: + succ = _state.ParameterNavigator.MoveToFirstAttribute(); + break; + case StatePosition.Parameter: + if (_macro.Parameters[_state.ParameterIndex].Attributes != null) + { + _state.Position = StatePosition.ParameterAttribute; + _state.ParameterAttributeIndex = 0; + succ = true; + DebugState(); + } + else succ = false; + break; + case StatePosition.ParameterAttribute: + case StatePosition.ParameterNodes: + case StatePosition.Macro: + case StatePosition.ParameterText: + case StatePosition.Root: + succ = false; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn(succ); + return succ; + } + + /// + /// Moves the XPathNavigator to the first child node of the current node. + /// + /// Returns true if the XPathNavigator is successful moving to the first child node of the current node; + /// otherwise, false. If false, the position of the XPathNavigator is unchanged. + public override bool MoveToFirstChild() + { + DebugEnter("MoveToFirstChild"); + bool succ; + + switch (_state.Position) + { + case StatePosition.Macro: + if (_macro.Parameters.Length == 0) + { + succ = false; + } + else + { + _state.ParameterIndex = 0; + _state.Position = StatePosition.Parameter; + succ = true; + } + break; + case StatePosition.Parameter: + var parameter = _macro.Parameters[_state.ParameterIndex]; + var nav = parameter.NavigatorValue; + if (parameter.WrapNavigatorInNodes) + { + _state.Position = StatePosition.ParameterNodes; + DebugState(); + succ = true; + } + else if (nav != null) + { + nav = nav.Clone(); // never use the raw parameter's navigator + nav.MoveToFirstChild(); + _state.ParameterNavigator = nav; + _state.ParameterNavigatorDepth = 0; + _state.Position = StatePosition.ParameterNavigator; + DebugState(); + succ = true; + } + else + { + var s = _macro.Parameters[_state.ParameterIndex].StringValue; + if (s != null) + { + _state.Position = StatePosition.ParameterText; + DebugState(); + succ = true; + } + else succ = false; + } + break; + case StatePosition.ParameterNavigator: + if (_state.ParameterNavigatorDepth == _macro.Parameters[_state.ParameterIndex].MaxNavigatorDepth) + { + succ = false; + } + else + { + // move to first doc child => increment depth, else (property child) do nothing + succ = _state.ParameterNavigator.MoveToFirstChild(); + if (succ && IsDoc(_state.ParameterNavigator)) + { + ++_state.ParameterNavigatorDepth; + DebugState(); + } + } + break; + case StatePosition.ParameterNodes: + if (_macro.Parameters[_state.ParameterIndex].NavigatorValue != null) + { + // never use the raw parameter's navigator + _state.ParameterNavigator = _macro.Parameters[_state.ParameterIndex].NavigatorValue.Clone(); + _state.Position = StatePosition.ParameterNavigator; + succ = true; + DebugState(); + } + else succ = false; + break; + case StatePosition.ParameterAttribute: + case StatePosition.ParameterText: + succ = false; + break; + case StatePosition.Root: + _state.Position = StatePosition.Macro; + DebugState(); + succ = true; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn(succ); + return succ; + } + + /// + /// Moves the XPathNavigator to the first namespace node that matches the XPathNamespaceScope specified. + /// + /// An XPathNamespaceScope value describing the namespace scope. + /// Returns true if the XPathNavigator is successful moving to the first namespace node; + /// otherwise, false. If false, the position of the XPathNavigator is unchanged. + public override bool MoveToFirstNamespace(XPathNamespaceScope namespaceScope) + { + DebugEnter("MoveToFirstNamespace"); + DebugReturn(false); + return false; + } + + /// + /// Moves the XPathNavigator to the next namespace node matching the XPathNamespaceScope specified. + /// + /// An XPathNamespaceScope value describing the namespace scope. + /// Returns true if the XPathNavigator is successful moving to the next namespace node; + /// otherwise, false. If false, the position of the XPathNavigator is unchanged. + public override bool MoveToNextNamespace(XPathNamespaceScope namespaceScope) + { + DebugEnter("MoveToNextNamespace"); + DebugReturn(false); + return false; + } + + /// + /// Moves to the node that has an attribute of type ID whose value matches the specified String. + /// + /// A String representing the ID value of the node to which you want to move. + /// true if the XPathNavigator is successful moving; otherwise, false. + /// If false, the position of the navigator is unchanged. + public override bool MoveToId(string id) + { + DebugEnter("MoveToId"); + // impossible to implement since parameters can contain duplicate fragments of the + // main xml and therefore there can be duplicate unique node identifiers. + DebugReturn("NotImplementedException"); + throw new NotImplementedException(); + } + + /// + /// Moves the XPathNavigator to the next sibling node of the current node. + /// + /// true if the XPathNavigator is successful moving to the next sibling node; + /// otherwise, false if there are no more siblings or if the XPathNavigator is currently + /// positioned on an attribute node. If false, the position of the XPathNavigator is unchanged. + public override bool MoveToNext() + { + DebugEnter("MoveToNext"); + bool succ; + + switch (_state.Position) + { + case StatePosition.Parameter: + if (_state.ParameterIndex == _macro.Parameters.Length - 1) + { + succ = false; + } + else + { + ++_state.ParameterIndex; + DebugState(); + succ = true; + } + break; + case StatePosition.ParameterNavigator: + var wasDoc = IsDoc(_state.ParameterNavigator); + succ = _state.ParameterNavigator.MoveToNext(); + if (succ && !wasDoc && IsDoc(_state.ParameterNavigator)) + { + // move to first doc child => increment depth, else (another property child) do nothing + if (_state.ParameterNavigatorDepth == _macro.Parameters[_state.ParameterIndex].MaxNavigatorDepth) + { + _state.ParameterNavigator.MoveToPrevious(); + succ = false; + } + else + { + ++_state.ParameterNavigatorDepth; + DebugState(); + } + } + break; + case StatePosition.ParameterNodes: + case StatePosition.ParameterAttribute: + case StatePosition.ParameterText: + case StatePosition.Macro: + case StatePosition.Root: + succ = false; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn(succ); + return succ; + } + + /// + /// Moves the XPathNavigator to the previous sibling node of the current node. + /// + /// Returns true if the XPathNavigator is successful moving to the previous sibling node; + /// otherwise, false if there is no previous sibling node or if the XPathNavigator is currently + /// positioned on an attribute node. If false, the position of the XPathNavigator is unchanged. + public override bool MoveToPrevious() + { + DebugEnter("MoveToPrevious"); + bool succ; + + switch (_state.Position) + { + case StatePosition.Parameter: + if (_state.ParameterIndex == -1) + { + succ = false; + } + else + { + --_state.ParameterIndex; + DebugState(); + succ = true; + } + break; + case StatePosition.ParameterNavigator: + var wasDoc = IsDoc(_state.ParameterNavigator); + succ = _state.ParameterNavigator.MoveToPrevious(); + if (succ && wasDoc && !IsDoc(_state.ParameterNavigator)) + { + // move from doc child back to property child => decrement depth + --_state.ParameterNavigatorDepth; + DebugState(); + } + break; + case StatePosition.ParameterAttribute: + case StatePosition.ParameterNodes: + case StatePosition.ParameterText: + case StatePosition.Macro: + case StatePosition.Root: + succ = false; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn(succ); + return succ; + } + + /// + /// Moves the XPathNavigator to the next attribute. + /// + /// Returns true if the XPathNavigator is successful moving to the next attribute; + /// false if there are no more attributes. If false, the position of the XPathNavigator is unchanged. + public override bool MoveToNextAttribute() + { + DebugEnter("MoveToNextAttribute"); + bool succ; + + switch (_state.Position) + { + case StatePosition.ParameterNavigator: + succ = _state.ParameterNavigator.MoveToNextAttribute(); + break; + case StatePosition.ParameterAttribute: + if (_state.ParameterAttributeIndex == _macro.Parameters[_state.ParameterIndex].Attributes.Length - 1) + succ = false; + else + { + ++_state.ParameterAttributeIndex; + DebugState(); + succ = true; + } + break; + case StatePosition.Parameter: + case StatePosition.ParameterNodes: + case StatePosition.ParameterText: + case StatePosition.Macro: + case StatePosition.Root: + succ = false; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn(succ); + return succ; + } + + /// + /// Moves the XPathNavigator to the parent node of the current node. + /// + /// Returns true if the XPathNavigator is successful moving to the parent node of the current node; + /// otherwise, false. If false, the position of the XPathNavigator is unchanged. + public override bool MoveToParent() + { + DebugEnter("MoveToParent"); + bool succ; + + switch (_state.Position) + { + case StatePosition.Macro: + _state.Position = StatePosition.Root; + DebugState(); + succ = true; + break; + case StatePosition.Parameter: + _state.Position = StatePosition.Macro; + DebugState(); + succ = true; + break; + case StatePosition.ParameterAttribute: + case StatePosition.ParameterNodes: + _state.Position = StatePosition.Parameter; + DebugState(); + succ = true; + break; + case StatePosition.ParameterNavigator: + var wasDoc = IsDoc(_state.ParameterNavigator); + succ = _state.ParameterNavigator.MoveToParent(); + if (succ) + { + // move from doc child => decrement depth + if (wasDoc && --_state.ParameterNavigatorDepth == 0) + { + _state.Position = StatePosition.Parameter; + _state.ParameterNavigator = null; + DebugState(); + } + } + break; + case StatePosition.ParameterText: + _state.Position = StatePosition.Parameter; + DebugState(); + succ = true; + break; + case StatePosition.Root: + succ = false; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn(succ); + return succ; + } + + /// + /// Moves the XPathNavigator to the root node that the current node belongs to. + /// + public override void MoveToRoot() + { + DebugEnter("MoveToRoot"); + + switch (_state.Position) + { + case StatePosition.ParameterNavigator: + _state.ParameterNavigator = null; + _state.ParameterNavigatorDepth = -1; + break; + case StatePosition.Parameter: + case StatePosition.ParameterText: + _state.ParameterIndex = -1; + break; + case StatePosition.ParameterAttribute: + case StatePosition.ParameterNodes: + case StatePosition.Macro: + case StatePosition.Root: + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + _state.Position = StatePosition.Root; + DebugState(); + + DebugReturn(); + } + + /// + /// Gets the base URI for the current node. + /// + public override string BaseURI + { + get { return string.Empty; } + } + + /// + /// Gets the XmlNameTable of the XPathNavigator. + /// + public override XmlNameTable NameTable + { + get { return _nameTable; } + } + + /// + /// Gets the namespace URI of the current node. + /// + public override string NamespaceURI + { + get { return string.Empty; } + } + + /// + /// Gets the XPathNodeType of the current node. + /// + public override XPathNodeType NodeType + { + get + { + DebugEnter("NodeType"); + XPathNodeType type; + + switch (_state.Position) + { + case StatePosition.Macro: + case StatePosition.Parameter: + case StatePosition.ParameterNodes: + type = XPathNodeType.Element; + break; + case StatePosition.ParameterNavigator: + type = _state.ParameterNavigator.NodeType; + break; + case StatePosition.ParameterAttribute: + type = XPathNodeType.Attribute; + break; + case StatePosition.ParameterText: + type = XPathNodeType.Text; + break; + case StatePosition.Root: + type = XPathNodeType.Root; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn("\'{0}\'", type); + return type; + } + } + + /// + /// Gets the namespace prefix associated with the current node. + /// + public override string Prefix + { + get { return string.Empty; } + } + + /// + /// Gets the string value of the item. + /// + /// Does not fully behave as per the specs, as we report empty value on root and macro elements, and we start + /// reporting values only on parameter elements. This is because, otherwise, we would might dump the whole database + /// and it probably does not make sense at Umbraco level. + public override string Value + { + get + { + DebugEnter("Value"); + string value; + + XPathNavigator nav; + switch (_state.Position) + { + case StatePosition.Parameter: + nav = _macro.Parameters[_state.ParameterIndex].NavigatorValue; + if (nav != null) + { + nav = nav.Clone(); // never use the raw parameter's navigator + nav.MoveToFirstChild(); + value = nav.Value; + } + else + { + var s = _macro.Parameters[_state.ParameterIndex].StringValue; + value = s ?? string.Empty; + } + break; + case StatePosition.ParameterAttribute: + value = _macro.Parameters[_state.ParameterIndex].Attributes[_state.ParameterAttributeIndex].Value; + break; + case StatePosition.ParameterNavigator: + value = _state.ParameterNavigator.Value; + break; + case StatePosition.ParameterNodes: + nav = _macro.Parameters[_state.ParameterIndex].NavigatorValue; + if (nav == null) + value = string.Empty; + else + { + nav = nav.Clone(); // never use the raw parameter's navigator + nav.MoveToFirstChild(); + value = nav.Value; + } + break; + case StatePosition.ParameterText: + value = _macro.Parameters[_state.ParameterIndex].StringValue; + break; + case StatePosition.Macro: + case StatePosition.Root: + value = string.Empty; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn("\"{0}\"", value); + return value; + } + } + + private static bool IsDoc(XPathNavigator nav) + { + if (nav.NodeType != XPathNodeType.Element) + return false; + + var clone = nav.Clone(); + if (!clone.MoveToFirstAttribute()) + return false; + do + { + if (clone.Name == "isDoc") + return true; + } while (clone.MoveToNextAttribute()); + + return false; + } + + #region State management + + // the possible state positions + internal enum StatePosition + { + Root, + Macro, + Parameter, + ParameterAttribute, + ParameterText, + ParameterNodes, + ParameterNavigator + }; + + // gets the state + // for unit tests only + internal State InternalState { get { return _state; } } + + // represents the XPathNavigator state + internal class State + { + public StatePosition Position { get; set; } + + // initialize a new state + private State(StatePosition position) + { + Position = position; + ParameterIndex = 0; + ParameterNavigatorDepth = 0; + ParameterAttributeIndex = 0; + } + + // initialize a new state + // used for creating the very first state + public State() + : this(StatePosition.Root) + { } + + // initialize a clone state + private State(State other) + { + Position = other.Position; + + ParameterIndex = other.ParameterIndex; + + if (Position == StatePosition.ParameterNavigator) + { + ParameterNavigator = other.ParameterNavigator.Clone(); + ParameterNavigatorDepth = other.ParameterNavigatorDepth; + ParameterAttributeIndex = other.ParameterAttributeIndex; + } + } + + public State Clone() + { + return new State(this); + } + + // the index of the current element + public int ParameterIndex { get; set; } + + // the current depth within the element navigator + public int ParameterNavigatorDepth { get; set; } + + // the index of the current element's attribute + public int ParameterAttributeIndex { get; set; } + + // gets or sets the element navigator + public XPathNavigator ParameterNavigator { get; set; } + + // gets a value indicating whether this state is at the same position as another one. + public bool IsSamePosition(State other) + { + return other.Position == Position + && (Position != StatePosition.ParameterNavigator || other.ParameterNavigator.IsSamePosition(ParameterNavigator)) + && other.ParameterIndex == ParameterIndex + && other.ParameterAttributeIndex == ParameterAttributeIndex; + } + } + + #endregion + } +} diff --git a/src/Umbraco.Core/Xml/XPath/NavigableNavigator.cs b/src/Umbraco.Core/Xml/XPath/NavigableNavigator.cs index a17727d4ac..c65e655d38 100644 --- a/src/Umbraco.Core/Xml/XPath/NavigableNavigator.cs +++ b/src/Umbraco.Core/Xml/XPath/NavigableNavigator.cs @@ -1,1149 +1,1149 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Xml; -using System.Xml.XPath; - -namespace Umbraco.Core.Xml.XPath -{ - /// - /// Provides a cursor model for navigating Umbraco data as if it were XML. - /// - class NavigableNavigator : XPathNavigator - { - // "The XmlNameTable stores atomized strings of any local name, namespace URI, - // and prefix used by the XPathNavigator. This means that when the same Name is - // returned multiple times (like "book"), the same String object is returned for - // that Name. This makes it possible to write efficient code that does object - // comparisons on these strings, instead of expensive string comparisons." - // - // "When an element or attribute name occurs multiple times in an XML document, - // it is stored only once in the NameTable. The names are stored as common - // language runtime (CLR) object types. This enables you to do object comparisons - // on these strings rather than a more expensive string comparison. These - // string objects are referred to as atomized strings." - // - // But... "Any instance members are not guaranteed to be thread safe." - // - // see http://msdn.microsoft.com/en-us/library/aa735772%28v=vs.71%29.aspx - // see http://www.hanselman.com/blog/XmlAndTheNametable.aspx - // see http://blogs.msdn.com/b/mfussell/archive/2004/04/30/123673.aspx - // - // "Additionally, all LocalName, NameSpaceUri and Prefix strings must be added to - // a NameTable, given by the NameTable property. When the LocalName, NamespaceURI, - // and Prefix properties are returned, the string returned should come from the - // NameTable. Comparisons between names are done by object comparisons rather - // than by string comparisons, which are significantly slower."" - // - // So what shall we do? Well, here we have no namespace, no prefix, and all - // local names come from cached instances of INavigableContentType or - // INavigableFieldType and are already unique. So... create a one nametable - // because we need one, and share it amongst all clones. - - private readonly XmlNameTable _nameTable; - private readonly INavigableSource _source; - private readonly int _lastAttributeIndex; // last index of attributes in the fields collection - private State _state; - - #region Constructor - - /// - /// Initializes a new instance of the class with a content source. - /// - private NavigableNavigator(INavigableSource source) - { - _source = source; - _lastAttributeIndex = source.LastAttributeIndex; - } - - /// - /// Initializes a new instance of the class with a content source, - /// and an optional root content. - /// - /// The content source. - /// The root content. - /// When no root content is supplied then the root of the source is used. - public NavigableNavigator(INavigableSource source, INavigableContent content = null) - : this(source) - { - _nameTable = new NameTable(); - _lastAttributeIndex = source.LastAttributeIndex; - _state = new State(content ?? source.Root, null, null, 0, StatePosition.Root); - } - - /// - /// Initializes a new instance of the class with a content source, a name table and a state. - /// - /// The content source. - /// The name table. - /// The state. - /// Privately used for cloning a navigator. - private NavigableNavigator(INavigableSource source, XmlNameTable nameTable, State state) - : this(source) - { - _nameTable = nameTable; - _state = state; - } - - #endregion - - #region Diagnostics - - // diagnostics code will not be compiled nor called into Release configuration. - // in Debug configuration, uncomment lines in Debug() to write to console or to log. - -#if DEBUG - private const string Tabs = " "; - private int _tabs; - private readonly int _uid = GetUid(); - private static int _uidg; - private readonly static object Uidl = new object(); - private static int GetUid() - { - lock (Uidl) - { - return _uidg++; - } - } -#endif - - [Conditional("DEBUG")] - void DebugEnter(string name) - { -#if DEBUG - Debug(""); - DebugState(":"); - Debug(name); - _tabs = Math.Min(Tabs.Length, _tabs + 2); -#endif - } - - [Conditional("DEBUG")] - void DebugCreate(NavigableNavigator nav) - { -#if DEBUG - Debug("Create: [NavigableNavigator::{0}]", nav._uid); -#endif - } - - [Conditional("DEBUG")] - private void DebugReturn() - { -#if DEBUG -// ReSharper disable IntroduceOptionalParameters.Local - DebugReturn("(void)"); -// ReSharper restore IntroduceOptionalParameters.Local -#endif - } - - [Conditional("DEBUG")] - private void DebugReturn(bool value) - { -#if DEBUG - DebugReturn(value ? "true" : "false"); -#endif - } - - [Conditional("DEBUG")] - void DebugReturn(string format, params object[] args) - { -#if DEBUG - Debug("=> " + format, args); - if (_tabs > 0) _tabs -= 2; -#endif - } - - [Conditional("DEBUG")] - void DebugState(string s = " =>") - { -#if DEBUG - string position; - - switch (_state.Position) - { - case StatePosition.Attribute: - position = string.Format("At attribute '{0}/@{1}'.", - _state.Content.Type.Name, - _state.FieldIndex < 0 ? "id" : _state.CurrentFieldType.Name); - break; - case StatePosition.Element: - position = string.Format("At element '{0}'.", - _state.Content.Type.Name); - break; - case StatePosition.PropertyElement: - position = string.Format("At property '{0}/{1}'.", - _state.Content.Type.Name, _state.Content.Type.FieldTypes[this._state.FieldIndex].Name); - break; - case StatePosition.PropertyText: - position = string.Format("At property '{0}/{1}' text.", - _state.Content.Type.Name, _state.CurrentFieldType.Name); - break; - case StatePosition.PropertyXml: - position = string.Format("In property '{0}/{1}' xml fragment.", - _state.Content.Type.Name, _state.CurrentFieldType.Name); - break; - case StatePosition.Root: - position = "At root."; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - Debug("State{0} {1}", s, position); -#endif - } - -#if DEBUG - void Debug(string format, params object[] args) - { - // remove comments to write - - format = "[" + _uid.ToString("00000") + "] " + Tabs.Substring(0, _tabs) + format; -#pragma warning disable 168 - var msg = string.Format(format, args); // unused if not writing, hence #pragma -#pragma warning restore 168 - //LogHelper.Debug(msg); // beware! this can quicky overflow log4net - //Console.WriteLine(msg); - } -#endif - - #endregion - - /// - /// Gets the underlying content object. - /// - public override object UnderlyingObject - { - get { return _state.Content; } - } - - /// - /// Creates a new XPathNavigator positioned at the same node as this XPathNavigator. - /// - /// A new XPathNavigator positioned at the same node as this XPathNavigator. - public override XPathNavigator Clone() - { - DebugEnter("Clone"); - var nav = new NavigableNavigator(_source, _nameTable, _state.Clone()); - DebugCreate(nav); - DebugReturn("[XPathNavigator]"); - return nav; - } - - /// - /// Creates a new XPathNavigator using the same source but positioned at a new root. - /// - /// A new XPathNavigator using the same source and positioned at a new root. - /// The new root can be above this navigator's root. - public XPathNavigator CloneWithNewRoot(string id) - { - DebugEnter("CloneWithNewRoot"); - - int contentId; - State state = null; - - if (id != null && id.Trim() == "-1") - { - state = new State(_source.Root, null, null, 0, StatePosition.Root); - } - else if (int.TryParse(id, out contentId)) - { - var content = _source.Get(contentId); - if (content != null) - { - state = new State(content, null, null, 0, StatePosition.Root); - } - } - - NavigableNavigator clone = null; - - if (state != null) - { - clone = new NavigableNavigator(_source, _nameTable, state); - DebugCreate(clone); - DebugReturn("[XPathNavigator]"); - } - else - { - DebugReturn("[null]"); - } - - return clone; - } - - /// - /// Gets a value indicating whether the current node is an empty element without an end element tag. - /// - public override bool IsEmptyElement - { - get - { - DebugEnter("IsEmptyElement"); - bool isEmpty; - - switch (_state.Position) - { - case StatePosition.Element: - isEmpty = (_state.Content.ChildIds == null || _state.Content.ChildIds.Count == 0) // no content child - && _state.FieldsCount - 1 == _lastAttributeIndex; // no property element child - break; - case StatePosition.PropertyElement: - // value should be - // - an XPathNavigator over a non-empty XML fragment - // - a non-Xml-whitespace string - // - null - isEmpty = _state.Content.Value(_state.FieldIndex) == null; - break; - case StatePosition.PropertyXml: - isEmpty = _state.XmlFragmentNavigator.IsEmptyElement; - break; - case StatePosition.Attribute: - case StatePosition.PropertyText: - case StatePosition.Root: - throw new InvalidOperationException("Not an element."); - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn(isEmpty); - return isEmpty; - } - } - - /// - /// Determines whether the current XPathNavigator is at the same position as the specified XPathNavigator. - /// - /// The XPathNavigator to compare to this XPathNavigator. - /// true if the two XPathNavigator objects have the same position; otherwise, false. - public override bool IsSamePosition(XPathNavigator nav) - { - DebugEnter("IsSamePosition"); - bool isSame; - - switch (_state.Position) - { - case StatePosition.PropertyXml: - isSame = _state.XmlFragmentNavigator.IsSamePosition(nav); - break; - case StatePosition.Attribute: - case StatePosition.Element: - case StatePosition.PropertyElement: - case StatePosition.PropertyText: - case StatePosition.Root: - var other = nav as NavigableNavigator; - isSame = other != null && other._source == _source && _state.IsSamePosition(other._state); - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn(isSame); - return isSame; - } - - /// - /// Gets the qualified name of the current node. - /// - public override string Name - { - get - { - DebugEnter("Name"); - string name; - - switch (_state.Position) - { - case StatePosition.PropertyXml: - name = _state.XmlFragmentNavigator.Name; - break; - case StatePosition.Attribute: - case StatePosition.PropertyElement: - name = _state.FieldIndex == -1 ? "id" : _state.CurrentFieldType.Name; - break; - case StatePosition.Element: - name = _state.Content.Type.Name; - break; - case StatePosition.PropertyText: - name = string.Empty; - break; - case StatePosition.Root: - name = string.Empty; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn("\"{0}\"", name); - return name; - } - } - - /// - /// Gets the Name of the current node without any namespace prefix. - /// - public override string LocalName - { - get - { - DebugEnter("LocalName"); - var name = Name; - DebugReturn("\"{0}\"", name); - return name; - } - } - - /// - /// Moves the XPathNavigator to the same position as the specified XPathNavigator. - /// - /// The XPathNavigator positioned on the node that you want to move to. - /// Returns true if the XPathNavigator is successful moving to the same position as the specified XPathNavigator; - /// otherwise, false. If false, the position of the XPathNavigator is unchanged. - public override bool MoveTo(XPathNavigator nav) - { - DebugEnter("MoveTo"); - - var other = nav as NavigableNavigator; - var succ = false; - - if (other != null && other._source == _source) - { - _state = other._state.Clone(); - DebugState(); - succ = true; - } - - DebugReturn(succ); - return succ; - } - - /// - /// Moves the XPathNavigator to the first attribute of the current node. - /// - /// Returns true if the XPathNavigator is successful moving to the first attribute of the current node; - /// otherwise, false. If false, the position of the XPathNavigator is unchanged. - public override bool MoveToFirstAttribute() - { - DebugEnter("MoveToFirstAttribute"); - bool succ; - - switch (_state.Position) - { - case StatePosition.PropertyXml: - succ = _state.XmlFragmentNavigator.MoveToFirstAttribute(); - break; - case StatePosition.Element: - _state.FieldIndex = -1; - _state.Position = StatePosition.Attribute; - DebugState(); - succ = true; - break; - case StatePosition.Attribute: - case StatePosition.PropertyElement: - case StatePosition.PropertyText: - case StatePosition.Root: - succ = false; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn(succ); - return succ; - } - - /// - /// Moves the XPathNavigator to the first child node of the current node. - /// - /// Returns true if the XPathNavigator is successful moving to the first child node of the current node; - /// otherwise, false. If false, the position of the XPathNavigator is unchanged. - public override bool MoveToFirstChild() - { - DebugEnter("MoveToFirstChild"); - bool succ; - - switch (_state.Position) - { - case StatePosition.PropertyXml: - succ = _state.XmlFragmentNavigator.MoveToFirstChild(); - break; - case StatePosition.Attribute: - case StatePosition.PropertyText: - succ = false; - break; - case StatePosition.Element: - var firstPropertyIndex = _lastAttributeIndex + 1; - if (_state.FieldsCount > firstPropertyIndex) - { - _state.Position = StatePosition.PropertyElement; - _state.FieldIndex = firstPropertyIndex; - DebugState(); - succ = true; - } - else succ = MoveToFirstChildElement(); - break; - case StatePosition.PropertyElement: - succ = MoveToFirstChildProperty(); - break; - case StatePosition.Root: - _state.Position = StatePosition.Element; - DebugState(); - succ = true; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn(succ); - return succ; - } - - private bool MoveToFirstChildElement() - { - var children = _state.Content.ChildIds; - - if (children != null && children.Count > 0) - { - // children may contain IDs that does not correspond to some content in source - // because children contains all child IDs including unpublished children - and - // then if we're not previewing, the source will return null. - var child = children.Select(id => _source.Get(id)).FirstOrDefault(c => c != null); - if (child != null) - { - _state.Position = StatePosition.Element; - _state.FieldIndex = -1; - _state = new State(child, _state, children, 0, StatePosition.Element); - DebugState(); - return true; - } - } - - return false; - } - - private bool MoveToFirstChildProperty() - { - var valueForXPath = _state.Content.Value(_state.FieldIndex); - - // value should be - // - an XPathNavigator over a non-empty XML fragment - // - a non-Xml-whitespace string - // - null - - var nav = valueForXPath as XPathNavigator; - if (nav != null) - { - nav = nav.Clone(); // never use the one we got - nav.MoveToFirstChild(); - _state.XmlFragmentNavigator = nav; - _state.Position = StatePosition.PropertyXml; - DebugState(); - return true; - } - - if (valueForXPath == null) - return false; - - if (valueForXPath is string) - { - _state.Position = StatePosition.PropertyText; - DebugState(); - return true; - } - - throw new InvalidOperationException("XPathValue must be an XPathNavigator or a string."); - } - - /// - /// Moves the XPathNavigator to the first namespace node that matches the XPathNamespaceScope specified. - /// - /// An XPathNamespaceScope value describing the namespace scope. - /// Returns true if the XPathNavigator is successful moving to the first namespace node; - /// otherwise, false. If false, the position of the XPathNavigator is unchanged. - public override bool MoveToFirstNamespace(XPathNamespaceScope namespaceScope) - { - DebugEnter("MoveToFirstNamespace"); - DebugReturn(false); - return false; - } - - /// - /// Moves the XPathNavigator to the next namespace node matching the XPathNamespaceScope specified. - /// - /// An XPathNamespaceScope value describing the namespace scope. - /// Returns true if the XPathNavigator is successful moving to the next namespace node; - /// otherwise, false. If false, the position of the XPathNavigator is unchanged. - public override bool MoveToNextNamespace(XPathNamespaceScope namespaceScope) - { - DebugEnter("MoveToNextNamespace"); - DebugReturn(false); - return false; - } - - /// - /// Moves to the node that has an attribute of type ID whose value matches the specified String. - /// - /// A String representing the ID value of the node to which you want to move. - /// true if the XPathNavigator is successful moving; otherwise, false. - /// If false, the position of the navigator is unchanged. - public override bool MoveToId(string id) - { - DebugEnter("MoveToId"); - var succ = false; - - // don't look into fragments, just look for element identifiers - // not sure we actually need to implement it... think of it as - // as exercise of style, always better than throwing NotImplemented. - - int contentId; - if (/*id != null &&*/ id.Trim() == "-1") // id cannot be null - { - _state = new State(_source.Root, null, _source.Root.ChildIds, 0, StatePosition.Element); - succ = true; - } - else if (int.TryParse(id, out contentId)) - { - var content = _source.Get(contentId); - if (content != null) - { - var state = _state; - while (state.Parent != null) - state = state.Parent; - var navRootId = state.Content.Id; // navigator may be rooted below source root - - var s = new Stack(); - while (content != null && content.ParentId != navRootId) - { - s.Push(content); - content = _source.Get(content.ParentId); - } - if (content != null) - { - _state = new State(_source.Root, null, _source.Root.ChildIds, _source.Root.ChildIds.IndexOf(content.Id), StatePosition.Element); - while (content != null) - { - _state = new State(content, _state, content.ChildIds, _state.Content.ChildIds.IndexOf(content.Id), StatePosition.Element); - content = s.Count == 0 ? null : s.Pop(); - } - DebugState(); - succ = true; - } - } - } - - DebugReturn(succ); - return succ; - } - - /// - /// Moves the XPathNavigator to the next sibling node of the current node. - /// - /// true if the XPathNavigator is successful moving to the next sibling node; - /// otherwise, false if there are no more siblings or if the XPathNavigator is currently - /// positioned on an attribute node. If false, the position of the XPathNavigator is unchanged. - public override bool MoveToNext() - { - DebugEnter("MoveToNext"); - bool succ; - - switch (_state.Position) - { - case StatePosition.PropertyXml: - succ = _state.XmlFragmentNavigator.MoveToNext(); - break; - case StatePosition.Element: - succ = false; - while (_state.Siblings != null && _state.SiblingIndex < _state.Siblings.Count - 1) - { - // Siblings may contain IDs that does not correspond to some content in source - // because children contains all child IDs including unpublished children - and - // then if we're not previewing, the source will return null. - var node = _source.Get(_state.Siblings[++_state.SiblingIndex]); - if (node == null) continue; - - _state.Content = node; - DebugState(); - succ = true; - break; - } - break; - case StatePosition.PropertyElement: - if (_state.FieldIndex == _state.FieldsCount - 1) - { - // after property elements may come some children elements - // if successful, will push a new state - succ = MoveToFirstChildElement(); - } - else - { - ++_state.FieldIndex; - DebugState(); - succ = true; - } - break; - case StatePosition.PropertyText: - case StatePosition.Attribute: - case StatePosition.Root: - succ = false; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn(succ); - return succ; - } - - /// - /// Moves the XPathNavigator to the previous sibling node of the current node. - /// - /// Returns true if the XPathNavigator is successful moving to the previous sibling node; - /// otherwise, false if there is no previous sibling node or if the XPathNavigator is currently - /// positioned on an attribute node. If false, the position of the XPathNavigator is unchanged. - public override bool MoveToPrevious() - { - DebugEnter("MoveToPrevious"); - bool succ; - - switch (_state.Position) - { - case StatePosition.PropertyXml: - succ = _state.XmlFragmentNavigator.MoveToPrevious(); - break; - case StatePosition.Element: - succ = false; - while (_state.Siblings != null && _state.SiblingIndex > 0) - { - // children may contain IDs that does not correspond to some content in source - // because children contains all child IDs including unpublished children - and - // then if we're not previewing, the source will return null. - var content = _source.Get(_state.Siblings[--_state.SiblingIndex]); - if (content == null) continue; - - _state.Content = content; - DebugState(); - succ = true; - break; - } - if (succ == false && _state.SiblingIndex == 0 && _state.FieldsCount - 1 > _lastAttributeIndex) - { - // before children elements may come some property elements - if (MoveToParentElement()) // pops the state - { - _state.FieldIndex = _state.FieldsCount - 1; - DebugState(); - succ = true; - } - } - break; - case StatePosition.PropertyElement: - succ = false; - if (_state.FieldIndex > _lastAttributeIndex) - { - --_state.FieldIndex; - DebugState(); - succ = true; - } - break; - case StatePosition.Attribute: - case StatePosition.PropertyText: - case StatePosition.Root: - succ = false; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn(succ); - return succ; - } - - /// - /// Moves the XPathNavigator to the next attribute. - /// - /// Returns true if the XPathNavigator is successful moving to the next attribute; - /// false if there are no more attributes. If false, the position of the XPathNavigator is unchanged. - public override bool MoveToNextAttribute() - { - DebugEnter("MoveToNextAttribute"); - bool succ; - - switch (_state.Position) - { - case StatePosition.PropertyXml: - succ = _state.XmlFragmentNavigator.MoveToNextAttribute(); - break; - case StatePosition.Attribute: - if (_state.FieldIndex == _lastAttributeIndex) - succ = false; - else - { - ++_state.FieldIndex; - DebugState(); - succ = true; - } - break; - case StatePosition.Element: - case StatePosition.PropertyElement: - case StatePosition.PropertyText: - case StatePosition.Root: - succ = false; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn(succ); - return succ; - } - - /// - /// Moves the XPathNavigator to the parent node of the current node. - /// - /// Returns true if the XPathNavigator is successful moving to the parent node of the current node; - /// otherwise, false. If false, the position of the XPathNavigator is unchanged. - public override bool MoveToParent() - { - DebugEnter("MoveToParent"); - bool succ; - - switch (_state.Position) - { - case StatePosition.Attribute: - case StatePosition.PropertyElement: - _state.Position = StatePosition.Element; - _state.FieldIndex = -1; - DebugState(); - succ = true; - break; - case StatePosition.Element: - succ = MoveToParentElement(); - if (!succ) - { - _state.Position = StatePosition.Root; - succ = true; - } - break; - case StatePosition.PropertyText: - _state.Position = StatePosition.PropertyElement; - DebugState(); - succ = true; - break; - case StatePosition.PropertyXml: - if (!_state.XmlFragmentNavigator.MoveToParent()) - throw new InvalidOperationException("Could not move to parent in fragment."); - if (_state.XmlFragmentNavigator.NodeType == XPathNodeType.Root) - { - _state.XmlFragmentNavigator = null; - _state.Position = StatePosition.PropertyElement; - DebugState(); - } - succ = true; - break; - case StatePosition.Root: - succ = false; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn(succ); - return succ; - } - - private bool MoveToParentElement() - { - var p = _state.Parent; - if (p != null) - { - _state = p; - DebugState(); - return true; - } - - return false; - } - - /// - /// Moves the XPathNavigator to the root node that the current node belongs to. - /// - public override void MoveToRoot() - { - DebugEnter("MoveToRoot"); - - while (_state.Parent != null) - _state = _state.Parent; - DebugState(); - - DebugReturn(); - } - - /// - /// Gets the base URI for the current node. - /// - public override string BaseURI - { - get { return string.Empty; } - } - - /// - /// Gets the XmlNameTable of the XPathNavigator. - /// - public override XmlNameTable NameTable - { - get { return _nameTable; } - } - - /// - /// Gets the namespace URI of the current node. - /// - public override string NamespaceURI - { - get { return string.Empty; } - } - - /// - /// Gets the XPathNodeType of the current node. - /// - public override XPathNodeType NodeType - { - get - { - DebugEnter("NodeType"); - XPathNodeType type; - - switch (_state.Position) - { - case StatePosition.PropertyXml: - type = _state.XmlFragmentNavigator.NodeType; - break; - case StatePosition.Attribute: - type = XPathNodeType.Attribute; - break; - case StatePosition.Element: - case StatePosition.PropertyElement: - type = XPathNodeType.Element; - break; - case StatePosition.PropertyText: - type = XPathNodeType.Text; - break; - case StatePosition.Root: - type = XPathNodeType.Root; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn("\'{0}\'", type); - return type; - } - } - - /// - /// Gets the namespace prefix associated with the current node. - /// - public override string Prefix - { - get { return string.Empty; } - } - - /// - /// Gets the string value of the item. - /// - /// Does not fully behave as per the specs, as we report empty value on content elements, and we start - /// reporting values only on property elements. This is because, otherwise, we would dump the whole database - /// and it probably does not make sense at Umbraco level. - public override string Value - { - get - { - DebugEnter("Value"); - string value; - - switch (_state.Position) - { - case StatePosition.PropertyXml: - value = _state.XmlFragmentNavigator.Value; - break; - case StatePosition.Attribute: - case StatePosition.PropertyText: - case StatePosition.PropertyElement: - if (_state.FieldIndex == -1) - { - value = _state.Content.Id.ToString(CultureInfo.InvariantCulture); - } - else - { - var valueForXPath = _state.Content.Value(_state.FieldIndex); - - // value should be - // - an XPathNavigator over a non-empty XML fragment - // - a non-Xml-whitespace string - // - null - - var nav = valueForXPath as XPathNavigator; - var s = valueForXPath as string; - if (valueForXPath == null) - { - value = string.Empty; - } - else if (nav != null) - { - nav = nav.Clone(); // never use the one we got - value = nav.Value; - } - else if (s != null) - { - value = s; - } - else - { - throw new InvalidOperationException("XPathValue must be an XPathNavigator or a string."); - } - } - break; - case StatePosition.Element: - case StatePosition.Root: - value = string.Empty; - break; - default: - throw new InvalidOperationException("Invalid position."); - } - - DebugReturn("\"{0}\"", value); - return value; - } - } - - #region State management - - // the possible state positions - internal enum StatePosition - { - Root, - Element, - Attribute, - PropertyElement, - PropertyText, - PropertyXml - }; - - // gets the state - // for unit tests only - internal State InternalState { get { return _state; } } - - // represents the XPathNavigator state - internal class State - { - public StatePosition Position { get; set; } - - // initialize a new state - private State(StatePosition position) - { - Position = position; - FieldIndex = -1; - } - - // initialize a new state - // used for creating the very first state - // and also when moving to a child element - public State(INavigableContent content, State parent, IList siblings, int siblingIndex, StatePosition position) - : this(position) - { - Content = content; - Parent = parent; - Siblings = siblings; - SiblingIndex = siblingIndex; - } - - // initialize a clone state - private State(State other, bool recurse = false) - { - Position = other.Position; - - _content = other._content; - SiblingIndex = other.SiblingIndex; - Siblings = other.Siblings; - FieldsCount = other.FieldsCount; - FieldIndex = other.FieldIndex; - - if (Position == StatePosition.PropertyXml) - XmlFragmentNavigator = other.XmlFragmentNavigator.Clone(); - - // NielsK did - //Parent = other.Parent; - // but that creates corrupted stacks of states when cloning - // because clones share the parents : have to clone the whole - // stack of states. Avoid recursion. - - if (recurse) return; - - var clone = this; - while (other.Parent != null) - { - clone.Parent = new State(other.Parent, true); - clone = clone.Parent; - other = other.Parent; - } - } - - public State Clone() - { - return new State(this); - } - - // the parent state - public State Parent { get; private set; } - - // the current content - private INavigableContent _content; - - // the current content - public INavigableContent Content - { - get - { - return _content; - } - set - { - FieldsCount = value == null ? 0 : value.Type.FieldTypes.Length; - _content = value; - } - } - - // the index of the current content within Siblings - public int SiblingIndex { get; set; } - - // the list of content identifiers for all children of the current content's parent - public IList Siblings { get; set; } - - // the number of fields of the current content - // properties include attributes and properties - public int FieldsCount { get; private set; } - - // the index of the current field - // index -1 means special attribute "id" - public int FieldIndex { get; set; } - - // the current field type - // beware, no check on the index - public INavigableFieldType CurrentFieldType { get { return Content.Type.FieldTypes[FieldIndex]; } } - - // gets or sets the xml fragment navigator - public XPathNavigator XmlFragmentNavigator { get; set; } - - // gets a value indicating whether this state is at the same position as another one. - public bool IsSamePosition(State other) - { - return other.Position == Position - && (Position != StatePosition.PropertyXml || other.XmlFragmentNavigator.IsSamePosition(XmlFragmentNavigator)) - && other.Content == Content - && other.FieldIndex == FieldIndex; - } - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Xml; +using System.Xml.XPath; + +namespace Umbraco.Core.Xml.XPath +{ + /// + /// Provides a cursor model for navigating Umbraco data as if it were XML. + /// + class NavigableNavigator : XPathNavigator + { + // "The XmlNameTable stores atomized strings of any local name, namespace URI, + // and prefix used by the XPathNavigator. This means that when the same Name is + // returned multiple times (like "book"), the same String object is returned for + // that Name. This makes it possible to write efficient code that does object + // comparisons on these strings, instead of expensive string comparisons." + // + // "When an element or attribute name occurs multiple times in an XML document, + // it is stored only once in the NameTable. The names are stored as common + // language runtime (CLR) object types. This enables you to do object comparisons + // on these strings rather than a more expensive string comparison. These + // string objects are referred to as atomized strings." + // + // But... "Any instance members are not guaranteed to be thread safe." + // + // see http://msdn.microsoft.com/en-us/library/aa735772%28v=vs.71%29.aspx + // see http://www.hanselman.com/blog/XmlAndTheNametable.aspx + // see http://blogs.msdn.com/b/mfussell/archive/2004/04/30/123673.aspx + // + // "Additionally, all LocalName, NameSpaceUri and Prefix strings must be added to + // a NameTable, given by the NameTable property. When the LocalName, NamespaceURI, + // and Prefix properties are returned, the string returned should come from the + // NameTable. Comparisons between names are done by object comparisons rather + // than by string comparisons, which are significantly slower."" + // + // So what shall we do? Well, here we have no namespace, no prefix, and all + // local names come from cached instances of INavigableContentType or + // INavigableFieldType and are already unique. So... create a one nametable + // because we need one, and share it amongst all clones. + + private readonly XmlNameTable _nameTable; + private readonly INavigableSource _source; + private readonly int _lastAttributeIndex; // last index of attributes in the fields collection + private State _state; + + #region Constructor + + /// + /// Initializes a new instance of the class with a content source. + /// + private NavigableNavigator(INavigableSource source) + { + _source = source; + _lastAttributeIndex = source.LastAttributeIndex; + } + + /// + /// Initializes a new instance of the class with a content source, + /// and an optional root content. + /// + /// The content source. + /// The root content. + /// When no root content is supplied then the root of the source is used. + public NavigableNavigator(INavigableSource source, INavigableContent content = null) + : this(source) + { + _nameTable = new NameTable(); + _lastAttributeIndex = source.LastAttributeIndex; + _state = new State(content ?? source.Root, null, null, 0, StatePosition.Root); + } + + /// + /// Initializes a new instance of the class with a content source, a name table and a state. + /// + /// The content source. + /// The name table. + /// The state. + /// Privately used for cloning a navigator. + private NavigableNavigator(INavigableSource source, XmlNameTable nameTable, State state) + : this(source) + { + _nameTable = nameTable; + _state = state; + } + + #endregion + + #region Diagnostics + + // diagnostics code will not be compiled nor called into Release configuration. + // in Debug configuration, uncomment lines in Debug() to write to console or to log. + +#if DEBUG + private const string Tabs = " "; + private int _tabs; + private readonly int _uid = GetUid(); + private static int _uidg; + private readonly static object Uidl = new object(); + private static int GetUid() + { + lock (Uidl) + { + return _uidg++; + } + } +#endif + + [Conditional("DEBUG")] + void DebugEnter(string name) + { +#if DEBUG + Debug(""); + DebugState(":"); + Debug(name); + _tabs = Math.Min(Tabs.Length, _tabs + 2); +#endif + } + + [Conditional("DEBUG")] + void DebugCreate(NavigableNavigator nav) + { +#if DEBUG + Debug("Create: [NavigableNavigator::{0}]", nav._uid); +#endif + } + + [Conditional("DEBUG")] + private void DebugReturn() + { +#if DEBUG +// ReSharper disable IntroduceOptionalParameters.Local + DebugReturn("(void)"); +// ReSharper restore IntroduceOptionalParameters.Local +#endif + } + + [Conditional("DEBUG")] + private void DebugReturn(bool value) + { +#if DEBUG + DebugReturn(value ? "true" : "false"); +#endif + } + + [Conditional("DEBUG")] + void DebugReturn(string format, params object[] args) + { +#if DEBUG + Debug("=> " + format, args); + if (_tabs > 0) _tabs -= 2; +#endif + } + + [Conditional("DEBUG")] + void DebugState(string s = " =>") + { +#if DEBUG + string position; + + switch (_state.Position) + { + case StatePosition.Attribute: + position = string.Format("At attribute '{0}/@{1}'.", + _state.Content.Type.Name, + _state.FieldIndex < 0 ? "id" : _state.CurrentFieldType.Name); + break; + case StatePosition.Element: + position = string.Format("At element '{0}'.", + _state.Content.Type.Name); + break; + case StatePosition.PropertyElement: + position = string.Format("At property '{0}/{1}'.", + _state.Content.Type.Name, _state.Content.Type.FieldTypes[this._state.FieldIndex].Name); + break; + case StatePosition.PropertyText: + position = string.Format("At property '{0}/{1}' text.", + _state.Content.Type.Name, _state.CurrentFieldType.Name); + break; + case StatePosition.PropertyXml: + position = string.Format("In property '{0}/{1}' xml fragment.", + _state.Content.Type.Name, _state.CurrentFieldType.Name); + break; + case StatePosition.Root: + position = "At root."; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + Debug("State{0} {1}", s, position); +#endif + } + +#if DEBUG + void Debug(string format, params object[] args) + { + // remove comments to write + + format = "[" + _uid.ToString("00000") + "] " + Tabs.Substring(0, _tabs) + format; +#pragma warning disable 168 + var msg = string.Format(format, args); // unused if not writing, hence #pragma +#pragma warning restore 168 + //LogHelper.Debug(msg); // beware! this can quicky overflow log4net + //Console.WriteLine(msg); + } +#endif + + #endregion + + /// + /// Gets the underlying content object. + /// + public override object UnderlyingObject + { + get { return _state.Content; } + } + + /// + /// Creates a new XPathNavigator positioned at the same node as this XPathNavigator. + /// + /// A new XPathNavigator positioned at the same node as this XPathNavigator. + public override XPathNavigator Clone() + { + DebugEnter("Clone"); + var nav = new NavigableNavigator(_source, _nameTable, _state.Clone()); + DebugCreate(nav); + DebugReturn("[XPathNavigator]"); + return nav; + } + + /// + /// Creates a new XPathNavigator using the same source but positioned at a new root. + /// + /// A new XPathNavigator using the same source and positioned at a new root. + /// The new root can be above this navigator's root. + public XPathNavigator CloneWithNewRoot(string id) + { + DebugEnter("CloneWithNewRoot"); + + int contentId; + State state = null; + + if (id != null && id.Trim() == "-1") + { + state = new State(_source.Root, null, null, 0, StatePosition.Root); + } + else if (int.TryParse(id, out contentId)) + { + var content = _source.Get(contentId); + if (content != null) + { + state = new State(content, null, null, 0, StatePosition.Root); + } + } + + NavigableNavigator clone = null; + + if (state != null) + { + clone = new NavigableNavigator(_source, _nameTable, state); + DebugCreate(clone); + DebugReturn("[XPathNavigator]"); + } + else + { + DebugReturn("[null]"); + } + + return clone; + } + + /// + /// Gets a value indicating whether the current node is an empty element without an end element tag. + /// + public override bool IsEmptyElement + { + get + { + DebugEnter("IsEmptyElement"); + bool isEmpty; + + switch (_state.Position) + { + case StatePosition.Element: + isEmpty = (_state.Content.ChildIds == null || _state.Content.ChildIds.Count == 0) // no content child + && _state.FieldsCount - 1 == _lastAttributeIndex; // no property element child + break; + case StatePosition.PropertyElement: + // value should be + // - an XPathNavigator over a non-empty XML fragment + // - a non-Xml-whitespace string + // - null + isEmpty = _state.Content.Value(_state.FieldIndex) == null; + break; + case StatePosition.PropertyXml: + isEmpty = _state.XmlFragmentNavigator.IsEmptyElement; + break; + case StatePosition.Attribute: + case StatePosition.PropertyText: + case StatePosition.Root: + throw new InvalidOperationException("Not an element."); + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn(isEmpty); + return isEmpty; + } + } + + /// + /// Determines whether the current XPathNavigator is at the same position as the specified XPathNavigator. + /// + /// The XPathNavigator to compare to this XPathNavigator. + /// true if the two XPathNavigator objects have the same position; otherwise, false. + public override bool IsSamePosition(XPathNavigator nav) + { + DebugEnter("IsSamePosition"); + bool isSame; + + switch (_state.Position) + { + case StatePosition.PropertyXml: + isSame = _state.XmlFragmentNavigator.IsSamePosition(nav); + break; + case StatePosition.Attribute: + case StatePosition.Element: + case StatePosition.PropertyElement: + case StatePosition.PropertyText: + case StatePosition.Root: + var other = nav as NavigableNavigator; + isSame = other != null && other._source == _source && _state.IsSamePosition(other._state); + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn(isSame); + return isSame; + } + + /// + /// Gets the qualified name of the current node. + /// + public override string Name + { + get + { + DebugEnter("Name"); + string name; + + switch (_state.Position) + { + case StatePosition.PropertyXml: + name = _state.XmlFragmentNavigator.Name; + break; + case StatePosition.Attribute: + case StatePosition.PropertyElement: + name = _state.FieldIndex == -1 ? "id" : _state.CurrentFieldType.Name; + break; + case StatePosition.Element: + name = _state.Content.Type.Name; + break; + case StatePosition.PropertyText: + name = string.Empty; + break; + case StatePosition.Root: + name = string.Empty; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn("\"{0}\"", name); + return name; + } + } + + /// + /// Gets the Name of the current node without any namespace prefix. + /// + public override string LocalName + { + get + { + DebugEnter("LocalName"); + var name = Name; + DebugReturn("\"{0}\"", name); + return name; + } + } + + /// + /// Moves the XPathNavigator to the same position as the specified XPathNavigator. + /// + /// The XPathNavigator positioned on the node that you want to move to. + /// Returns true if the XPathNavigator is successful moving to the same position as the specified XPathNavigator; + /// otherwise, false. If false, the position of the XPathNavigator is unchanged. + public override bool MoveTo(XPathNavigator nav) + { + DebugEnter("MoveTo"); + + var other = nav as NavigableNavigator; + var succ = false; + + if (other != null && other._source == _source) + { + _state = other._state.Clone(); + DebugState(); + succ = true; + } + + DebugReturn(succ); + return succ; + } + + /// + /// Moves the XPathNavigator to the first attribute of the current node. + /// + /// Returns true if the XPathNavigator is successful moving to the first attribute of the current node; + /// otherwise, false. If false, the position of the XPathNavigator is unchanged. + public override bool MoveToFirstAttribute() + { + DebugEnter("MoveToFirstAttribute"); + bool succ; + + switch (_state.Position) + { + case StatePosition.PropertyXml: + succ = _state.XmlFragmentNavigator.MoveToFirstAttribute(); + break; + case StatePosition.Element: + _state.FieldIndex = -1; + _state.Position = StatePosition.Attribute; + DebugState(); + succ = true; + break; + case StatePosition.Attribute: + case StatePosition.PropertyElement: + case StatePosition.PropertyText: + case StatePosition.Root: + succ = false; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn(succ); + return succ; + } + + /// + /// Moves the XPathNavigator to the first child node of the current node. + /// + /// Returns true if the XPathNavigator is successful moving to the first child node of the current node; + /// otherwise, false. If false, the position of the XPathNavigator is unchanged. + public override bool MoveToFirstChild() + { + DebugEnter("MoveToFirstChild"); + bool succ; + + switch (_state.Position) + { + case StatePosition.PropertyXml: + succ = _state.XmlFragmentNavigator.MoveToFirstChild(); + break; + case StatePosition.Attribute: + case StatePosition.PropertyText: + succ = false; + break; + case StatePosition.Element: + var firstPropertyIndex = _lastAttributeIndex + 1; + if (_state.FieldsCount > firstPropertyIndex) + { + _state.Position = StatePosition.PropertyElement; + _state.FieldIndex = firstPropertyIndex; + DebugState(); + succ = true; + } + else succ = MoveToFirstChildElement(); + break; + case StatePosition.PropertyElement: + succ = MoveToFirstChildProperty(); + break; + case StatePosition.Root: + _state.Position = StatePosition.Element; + DebugState(); + succ = true; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn(succ); + return succ; + } + + private bool MoveToFirstChildElement() + { + var children = _state.Content.ChildIds; + + if (children != null && children.Count > 0) + { + // children may contain IDs that does not correspond to some content in source + // because children contains all child IDs including unpublished children - and + // then if we're not previewing, the source will return null. + var child = children.Select(id => _source.Get(id)).FirstOrDefault(c => c != null); + if (child != null) + { + _state.Position = StatePosition.Element; + _state.FieldIndex = -1; + _state = new State(child, _state, children, 0, StatePosition.Element); + DebugState(); + return true; + } + } + + return false; + } + + private bool MoveToFirstChildProperty() + { + var valueForXPath = _state.Content.Value(_state.FieldIndex); + + // value should be + // - an XPathNavigator over a non-empty XML fragment + // - a non-Xml-whitespace string + // - null + + var nav = valueForXPath as XPathNavigator; + if (nav != null) + { + nav = nav.Clone(); // never use the one we got + nav.MoveToFirstChild(); + _state.XmlFragmentNavigator = nav; + _state.Position = StatePosition.PropertyXml; + DebugState(); + return true; + } + + if (valueForXPath == null) + return false; + + if (valueForXPath is string) + { + _state.Position = StatePosition.PropertyText; + DebugState(); + return true; + } + + throw new InvalidOperationException("XPathValue must be an XPathNavigator or a string."); + } + + /// + /// Moves the XPathNavigator to the first namespace node that matches the XPathNamespaceScope specified. + /// + /// An XPathNamespaceScope value describing the namespace scope. + /// Returns true if the XPathNavigator is successful moving to the first namespace node; + /// otherwise, false. If false, the position of the XPathNavigator is unchanged. + public override bool MoveToFirstNamespace(XPathNamespaceScope namespaceScope) + { + DebugEnter("MoveToFirstNamespace"); + DebugReturn(false); + return false; + } + + /// + /// Moves the XPathNavigator to the next namespace node matching the XPathNamespaceScope specified. + /// + /// An XPathNamespaceScope value describing the namespace scope. + /// Returns true if the XPathNavigator is successful moving to the next namespace node; + /// otherwise, false. If false, the position of the XPathNavigator is unchanged. + public override bool MoveToNextNamespace(XPathNamespaceScope namespaceScope) + { + DebugEnter("MoveToNextNamespace"); + DebugReturn(false); + return false; + } + + /// + /// Moves to the node that has an attribute of type ID whose value matches the specified String. + /// + /// A String representing the ID value of the node to which you want to move. + /// true if the XPathNavigator is successful moving; otherwise, false. + /// If false, the position of the navigator is unchanged. + public override bool MoveToId(string id) + { + DebugEnter("MoveToId"); + var succ = false; + + // don't look into fragments, just look for element identifiers + // not sure we actually need to implement it... think of it as + // as exercise of style, always better than throwing NotImplemented. + + int contentId; + if (/*id != null &&*/ id.Trim() == "-1") // id cannot be null + { + _state = new State(_source.Root, null, _source.Root.ChildIds, 0, StatePosition.Element); + succ = true; + } + else if (int.TryParse(id, out contentId)) + { + var content = _source.Get(contentId); + if (content != null) + { + var state = _state; + while (state.Parent != null) + state = state.Parent; + var navRootId = state.Content.Id; // navigator may be rooted below source root + + var s = new Stack(); + while (content != null && content.ParentId != navRootId) + { + s.Push(content); + content = _source.Get(content.ParentId); + } + if (content != null) + { + _state = new State(_source.Root, null, _source.Root.ChildIds, _source.Root.ChildIds.IndexOf(content.Id), StatePosition.Element); + while (content != null) + { + _state = new State(content, _state, content.ChildIds, _state.Content.ChildIds.IndexOf(content.Id), StatePosition.Element); + content = s.Count == 0 ? null : s.Pop(); + } + DebugState(); + succ = true; + } + } + } + + DebugReturn(succ); + return succ; + } + + /// + /// Moves the XPathNavigator to the next sibling node of the current node. + /// + /// true if the XPathNavigator is successful moving to the next sibling node; + /// otherwise, false if there are no more siblings or if the XPathNavigator is currently + /// positioned on an attribute node. If false, the position of the XPathNavigator is unchanged. + public override bool MoveToNext() + { + DebugEnter("MoveToNext"); + bool succ; + + switch (_state.Position) + { + case StatePosition.PropertyXml: + succ = _state.XmlFragmentNavigator.MoveToNext(); + break; + case StatePosition.Element: + succ = false; + while (_state.Siblings != null && _state.SiblingIndex < _state.Siblings.Count - 1) + { + // Siblings may contain IDs that does not correspond to some content in source + // because children contains all child IDs including unpublished children - and + // then if we're not previewing, the source will return null. + var node = _source.Get(_state.Siblings[++_state.SiblingIndex]); + if (node == null) continue; + + _state.Content = node; + DebugState(); + succ = true; + break; + } + break; + case StatePosition.PropertyElement: + if (_state.FieldIndex == _state.FieldsCount - 1) + { + // after property elements may come some children elements + // if successful, will push a new state + succ = MoveToFirstChildElement(); + } + else + { + ++_state.FieldIndex; + DebugState(); + succ = true; + } + break; + case StatePosition.PropertyText: + case StatePosition.Attribute: + case StatePosition.Root: + succ = false; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn(succ); + return succ; + } + + /// + /// Moves the XPathNavigator to the previous sibling node of the current node. + /// + /// Returns true if the XPathNavigator is successful moving to the previous sibling node; + /// otherwise, false if there is no previous sibling node or if the XPathNavigator is currently + /// positioned on an attribute node. If false, the position of the XPathNavigator is unchanged. + public override bool MoveToPrevious() + { + DebugEnter("MoveToPrevious"); + bool succ; + + switch (_state.Position) + { + case StatePosition.PropertyXml: + succ = _state.XmlFragmentNavigator.MoveToPrevious(); + break; + case StatePosition.Element: + succ = false; + while (_state.Siblings != null && _state.SiblingIndex > 0) + { + // children may contain IDs that does not correspond to some content in source + // because children contains all child IDs including unpublished children - and + // then if we're not previewing, the source will return null. + var content = _source.Get(_state.Siblings[--_state.SiblingIndex]); + if (content == null) continue; + + _state.Content = content; + DebugState(); + succ = true; + break; + } + if (succ == false && _state.SiblingIndex == 0 && _state.FieldsCount - 1 > _lastAttributeIndex) + { + // before children elements may come some property elements + if (MoveToParentElement()) // pops the state + { + _state.FieldIndex = _state.FieldsCount - 1; + DebugState(); + succ = true; + } + } + break; + case StatePosition.PropertyElement: + succ = false; + if (_state.FieldIndex > _lastAttributeIndex) + { + --_state.FieldIndex; + DebugState(); + succ = true; + } + break; + case StatePosition.Attribute: + case StatePosition.PropertyText: + case StatePosition.Root: + succ = false; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn(succ); + return succ; + } + + /// + /// Moves the XPathNavigator to the next attribute. + /// + /// Returns true if the XPathNavigator is successful moving to the next attribute; + /// false if there are no more attributes. If false, the position of the XPathNavigator is unchanged. + public override bool MoveToNextAttribute() + { + DebugEnter("MoveToNextAttribute"); + bool succ; + + switch (_state.Position) + { + case StatePosition.PropertyXml: + succ = _state.XmlFragmentNavigator.MoveToNextAttribute(); + break; + case StatePosition.Attribute: + if (_state.FieldIndex == _lastAttributeIndex) + succ = false; + else + { + ++_state.FieldIndex; + DebugState(); + succ = true; + } + break; + case StatePosition.Element: + case StatePosition.PropertyElement: + case StatePosition.PropertyText: + case StatePosition.Root: + succ = false; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn(succ); + return succ; + } + + /// + /// Moves the XPathNavigator to the parent node of the current node. + /// + /// Returns true if the XPathNavigator is successful moving to the parent node of the current node; + /// otherwise, false. If false, the position of the XPathNavigator is unchanged. + public override bool MoveToParent() + { + DebugEnter("MoveToParent"); + bool succ; + + switch (_state.Position) + { + case StatePosition.Attribute: + case StatePosition.PropertyElement: + _state.Position = StatePosition.Element; + _state.FieldIndex = -1; + DebugState(); + succ = true; + break; + case StatePosition.Element: + succ = MoveToParentElement(); + if (!succ) + { + _state.Position = StatePosition.Root; + succ = true; + } + break; + case StatePosition.PropertyText: + _state.Position = StatePosition.PropertyElement; + DebugState(); + succ = true; + break; + case StatePosition.PropertyXml: + if (!_state.XmlFragmentNavigator.MoveToParent()) + throw new InvalidOperationException("Could not move to parent in fragment."); + if (_state.XmlFragmentNavigator.NodeType == XPathNodeType.Root) + { + _state.XmlFragmentNavigator = null; + _state.Position = StatePosition.PropertyElement; + DebugState(); + } + succ = true; + break; + case StatePosition.Root: + succ = false; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn(succ); + return succ; + } + + private bool MoveToParentElement() + { + var p = _state.Parent; + if (p != null) + { + _state = p; + DebugState(); + return true; + } + + return false; + } + + /// + /// Moves the XPathNavigator to the root node that the current node belongs to. + /// + public override void MoveToRoot() + { + DebugEnter("MoveToRoot"); + + while (_state.Parent != null) + _state = _state.Parent; + DebugState(); + + DebugReturn(); + } + + /// + /// Gets the base URI for the current node. + /// + public override string BaseURI + { + get { return string.Empty; } + } + + /// + /// Gets the XmlNameTable of the XPathNavigator. + /// + public override XmlNameTable NameTable + { + get { return _nameTable; } + } + + /// + /// Gets the namespace URI of the current node. + /// + public override string NamespaceURI + { + get { return string.Empty; } + } + + /// + /// Gets the XPathNodeType of the current node. + /// + public override XPathNodeType NodeType + { + get + { + DebugEnter("NodeType"); + XPathNodeType type; + + switch (_state.Position) + { + case StatePosition.PropertyXml: + type = _state.XmlFragmentNavigator.NodeType; + break; + case StatePosition.Attribute: + type = XPathNodeType.Attribute; + break; + case StatePosition.Element: + case StatePosition.PropertyElement: + type = XPathNodeType.Element; + break; + case StatePosition.PropertyText: + type = XPathNodeType.Text; + break; + case StatePosition.Root: + type = XPathNodeType.Root; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn("\'{0}\'", type); + return type; + } + } + + /// + /// Gets the namespace prefix associated with the current node. + /// + public override string Prefix + { + get { return string.Empty; } + } + + /// + /// Gets the string value of the item. + /// + /// Does not fully behave as per the specs, as we report empty value on content elements, and we start + /// reporting values only on property elements. This is because, otherwise, we would dump the whole database + /// and it probably does not make sense at Umbraco level. + public override string Value + { + get + { + DebugEnter("Value"); + string value; + + switch (_state.Position) + { + case StatePosition.PropertyXml: + value = _state.XmlFragmentNavigator.Value; + break; + case StatePosition.Attribute: + case StatePosition.PropertyText: + case StatePosition.PropertyElement: + if (_state.FieldIndex == -1) + { + value = _state.Content.Id.ToString(CultureInfo.InvariantCulture); + } + else + { + var valueForXPath = _state.Content.Value(_state.FieldIndex); + + // value should be + // - an XPathNavigator over a non-empty XML fragment + // - a non-Xml-whitespace string + // - null + + var nav = valueForXPath as XPathNavigator; + var s = valueForXPath as string; + if (valueForXPath == null) + { + value = string.Empty; + } + else if (nav != null) + { + nav = nav.Clone(); // never use the one we got + value = nav.Value; + } + else if (s != null) + { + value = s; + } + else + { + throw new InvalidOperationException("XPathValue must be an XPathNavigator or a string."); + } + } + break; + case StatePosition.Element: + case StatePosition.Root: + value = string.Empty; + break; + default: + throw new InvalidOperationException("Invalid position."); + } + + DebugReturn("\"{0}\"", value); + return value; + } + } + + #region State management + + // the possible state positions + internal enum StatePosition + { + Root, + Element, + Attribute, + PropertyElement, + PropertyText, + PropertyXml + }; + + // gets the state + // for unit tests only + internal State InternalState { get { return _state; } } + + // represents the XPathNavigator state + internal class State + { + public StatePosition Position { get; set; } + + // initialize a new state + private State(StatePosition position) + { + Position = position; + FieldIndex = -1; + } + + // initialize a new state + // used for creating the very first state + // and also when moving to a child element + public State(INavigableContent content, State parent, IList siblings, int siblingIndex, StatePosition position) + : this(position) + { + Content = content; + Parent = parent; + Siblings = siblings; + SiblingIndex = siblingIndex; + } + + // initialize a clone state + private State(State other, bool recurse = false) + { + Position = other.Position; + + _content = other._content; + SiblingIndex = other.SiblingIndex; + Siblings = other.Siblings; + FieldsCount = other.FieldsCount; + FieldIndex = other.FieldIndex; + + if (Position == StatePosition.PropertyXml) + XmlFragmentNavigator = other.XmlFragmentNavigator.Clone(); + + // NielsK did + //Parent = other.Parent; + // but that creates corrupted stacks of states when cloning + // because clones share the parents : have to clone the whole + // stack of states. Avoid recursion. + + if (recurse) return; + + var clone = this; + while (other.Parent != null) + { + clone.Parent = new State(other.Parent, true); + clone = clone.Parent; + other = other.Parent; + } + } + + public State Clone() + { + return new State(this); + } + + // the parent state + public State Parent { get; private set; } + + // the current content + private INavigableContent _content; + + // the current content + public INavigableContent Content + { + get + { + return _content; + } + set + { + FieldsCount = value == null ? 0 : value.Type.FieldTypes.Length; + _content = value; + } + } + + // the index of the current content within Siblings + public int SiblingIndex { get; set; } + + // the list of content identifiers for all children of the current content's parent + public IList Siblings { get; set; } + + // the number of fields of the current content + // properties include attributes and properties + public int FieldsCount { get; private set; } + + // the index of the current field + // index -1 means special attribute "id" + public int FieldIndex { get; set; } + + // the current field type + // beware, no check on the index + public INavigableFieldType CurrentFieldType { get { return Content.Type.FieldTypes[FieldIndex]; } } + + // gets or sets the xml fragment navigator + public XPathNavigator XmlFragmentNavigator { get; set; } + + // gets a value indicating whether this state is at the same position as another one. + public bool IsSamePosition(State other) + { + return other.Position == Position + && (Position != StatePosition.PropertyXml || other.XmlFragmentNavigator.IsSamePosition(XmlFragmentNavigator)) + && other.Content == Content + && other.FieldIndex == FieldIndex; + } + } + + #endregion + } +} diff --git a/src/Umbraco.Tests/Configurations/DashboardSettings/Dashboard.config b/src/Umbraco.Tests/Configurations/DashboardSettings/Dashboard.config index 2586682fdb..a9ffdf78ee 100644 --- a/src/Umbraco.Tests/Configurations/DashboardSettings/Dashboard.config +++ b/src/Umbraco.Tests/Configurations/DashboardSettings/Dashboard.config @@ -1,114 +1,114 @@ - - - -
- - settings - - - - views/dashboard/settings/settingsdashboardintro.html - - - views/dashboard/settings/settingsdashboardvideos.html - - -
- -
- - developer - - - - views/dashboard/developer/developerdashboardintro.html - - - views/dashboard/developer/developerdashboardvideos.html - - - - /umbraco/dashboard/ExamineManagement.ascx - -
- -
- - media - - - - views/dashboard/media/mediafolderbrowser.html - - - - - admin - - - views/dashboard/media/mediadashboardintro.html - - - views/dashboard/media/desktopmediauploader.html - - - views/dashboard/media/mediadashboardvideos.html - - -
- -
- - translator - hello - world - - - content - - - - admin - - - views/dashboard/default/startupdashboardintro.html - - - views/dashboard/default/startupdashboardkits.html - - editor - writer - - - - views/dashboard/default/startupdashboardvideos.html - - - - /umbraco/dashboard/latestEdits.ascx - - - - views/dashboard/changepassword.html - - -
- -
- - default - member - - - - views/dashboard/members/membersdashboardintro.html - - - /umbraco/members/membersearch.ascx - - - views/dashboard/members/membersdashboardvideos.html - - -
+ + + +
+ + settings + + + + views/dashboard/settings/settingsdashboardintro.html + + + views/dashboard/settings/settingsdashboardvideos.html + + +
+ +
+ + developer + + + + views/dashboard/developer/developerdashboardintro.html + + + views/dashboard/developer/developerdashboardvideos.html + + + + /umbraco/dashboard/ExamineManagement.ascx + +
+ +
+ + media + + + + views/dashboard/media/mediafolderbrowser.html + + + + + admin + + + views/dashboard/media/mediadashboardintro.html + + + views/dashboard/media/desktopmediauploader.html + + + views/dashboard/media/mediadashboardvideos.html + + +
+ +
+ + translator + hello + world + + + content + + + + admin + + + views/dashboard/default/startupdashboardintro.html + + + views/dashboard/default/startupdashboardkits.html + + editor + writer + + + + views/dashboard/default/startupdashboardvideos.html + + + + /umbraco/dashboard/latestEdits.ascx + + + + views/dashboard/changepassword.html + + +
+ +
+ + default + member + + + + views/dashboard/members/membersdashboardintro.html + + + /umbraco/members/membersearch.ascx + + + views/dashboard/members/membersdashboardvideos.html + + +
\ No newline at end of file diff --git a/src/Umbraco.Tests/Configurations/DashboardSettings/DashboardSettingsTests.cs b/src/Umbraco.Tests/Configurations/DashboardSettings/DashboardSettingsTests.cs index 613a697c70..d69f474b58 100644 --- a/src/Umbraco.Tests/Configurations/DashboardSettings/DashboardSettingsTests.cs +++ b/src/Umbraco.Tests/Configurations/DashboardSettings/DashboardSettingsTests.cs @@ -1,116 +1,116 @@ -using System.Configuration; -using System.IO; -using System.Linq; -using NUnit.Framework; -using Umbraco.Core.Configuration.Dashboard; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Tests.TestHelpers; - -namespace Umbraco.Tests.Configurations.DashboardSettings -{ - [TestFixture] - public class DashboardSettingsTests - { - [SetUp] - public void Init() - { - var config = new FileInfo(TestHelper.MapPathForTest("~/Configurations/DashboardSettings/web.config")); - - var fileMap = new ExeConfigurationFileMap() { ExeConfigFilename = config.FullName }; - var configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); - - SettingsSection = configuration.GetSection("umbracoConfiguration/dashBoard") as DashboardSection; - - Assert.IsNotNull(SettingsSection); - } - - protected IDashboardSection SettingsSection { get; private set; } - - [Test] - public void Test_Sections() - { - Assert.AreEqual(5, SettingsSection.Sections.Count()); - - Assert.AreEqual("StartupSettingsDashboardSection", SettingsSection.Sections.ElementAt(0).Alias); - Assert.AreEqual("StartupDeveloperDashboardSection", SettingsSection.Sections.ElementAt(1).Alias); - Assert.AreEqual("StartupMediaDashboardSection", SettingsSection.Sections.ElementAt(2).Alias); - Assert.AreEqual("StartupDashboardSection", SettingsSection.Sections.ElementAt(3).Alias); - Assert.AreEqual("StartupMemberDashboardSection", SettingsSection.Sections.ElementAt(4).Alias); - } - - [Test] - public void Test_Section_Area() - { - Assert.AreEqual("settings", SettingsSection.Sections.ElementAt(0).Areas.First()); - Assert.AreEqual("developer", SettingsSection.Sections.ElementAt(1).Areas.First()); - Assert.AreEqual("media", SettingsSection.Sections.ElementAt(2).Areas.First()); - Assert.AreEqual("content", SettingsSection.Sections.ElementAt(3).Areas.First()); - Assert.AreEqual("default", SettingsSection.Sections.ElementAt(4).Areas.First()); - Assert.AreEqual("member", SettingsSection.Sections.ElementAt(4).Areas.Last()); - } - - [Test] - public void Test_Section_Access() - { - - Assert.AreEqual(3, SettingsSection.Sections.ElementAt(3).AccessRights.Rules.Count()); - - Assert.AreEqual("translator", SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(0).Value); - Assert.AreEqual(AccessType.Deny, SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(0).Action); - Assert.AreEqual("hello", SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(1).Value); - Assert.AreEqual(AccessType.Grant, SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(1).Action); - Assert.AreEqual("world", SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(2).Value); - Assert.AreEqual(AccessType.GrantBySection, SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(2).Action); - } - - [Test] - public void Test_Section_Tabs() - { - Assert.AreEqual(1, SettingsSection.Sections.ElementAt(0).Tabs.Count()); - Assert.AreEqual(2, SettingsSection.Sections.ElementAt(1).Tabs.Count()); - Assert.AreEqual(3, SettingsSection.Sections.ElementAt(3).Tabs.Count()); - - } - - [Test] - public void Test_Tab() - { - Assert.AreEqual("Get Started", SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Caption); - Assert.AreEqual(2, SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.Count()); - } - - [Test] - public void Test_Tab_Access() - { - Assert.AreEqual(1, SettingsSection.Sections.ElementAt(2).Tabs.ElementAt(1).AccessRights.Rules.Count()); - Assert.AreEqual(AccessType.Grant, SettingsSection.Sections.ElementAt(2).Tabs.ElementAt(1).AccessRights.Rules.ElementAt(0).Action); - Assert.AreEqual("admin", SettingsSection.Sections.ElementAt(2).Tabs.ElementAt(1).AccessRights.Rules.ElementAt(0).Value); - } - - [Test] - public void Test_Control() - { - Assert.AreEqual(true, SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(0).ShowOnce); - Assert.AreEqual(true, SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(0).AddPanel); - Assert.AreEqual("hello", SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(0).PanelCaption); - Assert.AreEqual("views/dashboard/settings/settingsdashboardintro.html", - SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(0).ControlPath); - - Assert.AreEqual(false, SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(1).ShowOnce); - Assert.AreEqual(false, SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(1).AddPanel); - Assert.AreEqual("", SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(1).PanelCaption); - Assert.AreEqual("views/dashboard/settings/settingsdashboardvideos.html", - SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(1).ControlPath); - } - - [Test] - public void Test_Control_Access() - { - Assert.AreEqual(2, SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.Count()); - Assert.AreEqual(AccessType.Deny, SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.ElementAt(0).Action); - Assert.AreEqual("editor", SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.ElementAt(0).Value); - Assert.AreEqual(AccessType.Deny, SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.ElementAt(1).Action); - Assert.AreEqual("writer", SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.ElementAt(1).Value); - } - } +using System.Configuration; +using System.IO; +using System.Linq; +using NUnit.Framework; +using Umbraco.Core.Configuration.Dashboard; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Tests.TestHelpers; + +namespace Umbraco.Tests.Configurations.DashboardSettings +{ + [TestFixture] + public class DashboardSettingsTests + { + [SetUp] + public void Init() + { + var config = new FileInfo(TestHelper.MapPathForTest("~/Configurations/DashboardSettings/web.config")); + + var fileMap = new ExeConfigurationFileMap() { ExeConfigFilename = config.FullName }; + var configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); + + SettingsSection = configuration.GetSection("umbracoConfiguration/dashBoard") as DashboardSection; + + Assert.IsNotNull(SettingsSection); + } + + protected IDashboardSection SettingsSection { get; private set; } + + [Test] + public void Test_Sections() + { + Assert.AreEqual(5, SettingsSection.Sections.Count()); + + Assert.AreEqual("StartupSettingsDashboardSection", SettingsSection.Sections.ElementAt(0).Alias); + Assert.AreEqual("StartupDeveloperDashboardSection", SettingsSection.Sections.ElementAt(1).Alias); + Assert.AreEqual("StartupMediaDashboardSection", SettingsSection.Sections.ElementAt(2).Alias); + Assert.AreEqual("StartupDashboardSection", SettingsSection.Sections.ElementAt(3).Alias); + Assert.AreEqual("StartupMemberDashboardSection", SettingsSection.Sections.ElementAt(4).Alias); + } + + [Test] + public void Test_Section_Area() + { + Assert.AreEqual("settings", SettingsSection.Sections.ElementAt(0).Areas.First()); + Assert.AreEqual("developer", SettingsSection.Sections.ElementAt(1).Areas.First()); + Assert.AreEqual("media", SettingsSection.Sections.ElementAt(2).Areas.First()); + Assert.AreEqual("content", SettingsSection.Sections.ElementAt(3).Areas.First()); + Assert.AreEqual("default", SettingsSection.Sections.ElementAt(4).Areas.First()); + Assert.AreEqual("member", SettingsSection.Sections.ElementAt(4).Areas.Last()); + } + + [Test] + public void Test_Section_Access() + { + + Assert.AreEqual(3, SettingsSection.Sections.ElementAt(3).AccessRights.Rules.Count()); + + Assert.AreEqual("translator", SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(0).Value); + Assert.AreEqual(AccessType.Deny, SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(0).Action); + Assert.AreEqual("hello", SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(1).Value); + Assert.AreEqual(AccessType.Grant, SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(1).Action); + Assert.AreEqual("world", SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(2).Value); + Assert.AreEqual(AccessType.GrantBySection, SettingsSection.Sections.ElementAt(3).AccessRights.Rules.ElementAt(2).Action); + } + + [Test] + public void Test_Section_Tabs() + { + Assert.AreEqual(1, SettingsSection.Sections.ElementAt(0).Tabs.Count()); + Assert.AreEqual(2, SettingsSection.Sections.ElementAt(1).Tabs.Count()); + Assert.AreEqual(3, SettingsSection.Sections.ElementAt(3).Tabs.Count()); + + } + + [Test] + public void Test_Tab() + { + Assert.AreEqual("Get Started", SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Caption); + Assert.AreEqual(2, SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.Count()); + } + + [Test] + public void Test_Tab_Access() + { + Assert.AreEqual(1, SettingsSection.Sections.ElementAt(2).Tabs.ElementAt(1).AccessRights.Rules.Count()); + Assert.AreEqual(AccessType.Grant, SettingsSection.Sections.ElementAt(2).Tabs.ElementAt(1).AccessRights.Rules.ElementAt(0).Action); + Assert.AreEqual("admin", SettingsSection.Sections.ElementAt(2).Tabs.ElementAt(1).AccessRights.Rules.ElementAt(0).Value); + } + + [Test] + public void Test_Control() + { + Assert.AreEqual(true, SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(0).ShowOnce); + Assert.AreEqual(true, SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(0).AddPanel); + Assert.AreEqual("hello", SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(0).PanelCaption); + Assert.AreEqual("views/dashboard/settings/settingsdashboardintro.html", + SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(0).ControlPath); + + Assert.AreEqual(false, SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(1).ShowOnce); + Assert.AreEqual(false, SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(1).AddPanel); + Assert.AreEqual("", SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(1).PanelCaption); + Assert.AreEqual("views/dashboard/settings/settingsdashboardvideos.html", + SettingsSection.Sections.ElementAt(0).Tabs.ElementAt(0).Controls.ElementAt(1).ControlPath); + } + + [Test] + public void Test_Control_Access() + { + Assert.AreEqual(2, SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.Count()); + Assert.AreEqual(AccessType.Deny, SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.ElementAt(0).Action); + Assert.AreEqual("editor", SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.ElementAt(0).Value); + Assert.AreEqual(AccessType.Deny, SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.ElementAt(1).Action); + Assert.AreEqual("writer", SettingsSection.Sections.ElementAt(3).Tabs.ElementAt(0).Controls.ElementAt(1).AccessRights.Rules.ElementAt(1).Value); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Configurations/DashboardSettings/web.config b/src/Umbraco.Tests/Configurations/DashboardSettings/web.config index 8cf262cbff..03d78beee4 100644 --- a/src/Umbraco.Tests/Configurations/DashboardSettings/web.config +++ b/src/Umbraco.Tests/Configurations/DashboardSettings/web.config @@ -1,14 +1,14 @@ - - - - - -
- - - - - - - + + + + + +
+ + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/DistributedCallElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/DistributedCallElementDefaultTests.cs index 8961ad5662..e0b1814442 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/DistributedCallElementDefaultTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/DistributedCallElementDefaultTests.cs @@ -1,27 +1,27 @@ -using System.Linq; -using NUnit.Framework; - -namespace Umbraco.Tests.Configurations.UmbracoSettings -{ - [TestFixture] - public class DistributedCallElementDefaultTests : DistributedCallElementTests - { - protected override bool TestingDefaults - { - get { return true; } - } - - [Test] - public override void Enabled() - { - Assert.IsTrue(SettingsSection.DistributedCall.Enabled == false); - - } - - [Test] - public override void Servers() - { - Assert.IsTrue(SettingsSection.DistributedCall.Servers.Count() == 0); - } - } +using System.Linq; +using NUnit.Framework; + +namespace Umbraco.Tests.Configurations.UmbracoSettings +{ + [TestFixture] + public class DistributedCallElementDefaultTests : DistributedCallElementTests + { + protected override bool TestingDefaults + { + get { return true; } + } + + [Test] + public override void Enabled() + { + Assert.IsTrue(SettingsSection.DistributedCall.Enabled == false); + + } + + [Test] + public override void Servers() + { + Assert.IsTrue(SettingsSection.DistributedCall.Servers.Count() == 0); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/HelpElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/HelpElementDefaultTests.cs index c105cbd941..7f4e6481de 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/HelpElementDefaultTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/HelpElementDefaultTests.cs @@ -1,20 +1,20 @@ -using System.Linq; -using NUnit.Framework; - -namespace Umbraco.Tests.Configurations.UmbracoSettings -{ - [TestFixture] - public class HelpElementDefaultTests : HelpElementTests - { - protected override bool TestingDefaults - { - get { return true; } - } - - [Test] - public override void Links() - { - Assert.IsTrue(SettingsSection.Help.Links.Count() == 0); - } - } +using System.Linq; +using NUnit.Framework; + +namespace Umbraco.Tests.Configurations.UmbracoSettings +{ + [TestFixture] + public class HelpElementDefaultTests : HelpElementTests + { + protected override bool TestingDefaults + { + get { return true; } + } + + [Test] + public override void Links() + { + Assert.IsTrue(SettingsSection.Help.Links.Count() == 0); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/PackageRepositoriesElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/PackageRepositoriesElementDefaultTests.cs index a2ec3fc3b1..b10a35f5f4 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/PackageRepositoriesElementDefaultTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/PackageRepositoriesElementDefaultTests.cs @@ -1,23 +1,23 @@ -using System; -using System.Linq; -using NUnit.Framework; - -namespace Umbraco.Tests.Configurations.UmbracoSettings -{ - [TestFixture] - public class PackageRepositoriesElementDefaultTests : PackageRepositoriesElementTests - { - protected override bool TestingDefaults - { - get { return true; } - } - - [Test] - public override void Repositories() - { - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.Count() == 1); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).Id == Guid.Parse("65194810-1f85-11dd-bd0b-0800200c9a66")); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).Name == "Umbraco package Repository"); - } - } +using System; +using System.Linq; +using NUnit.Framework; + +namespace Umbraco.Tests.Configurations.UmbracoSettings +{ + [TestFixture] + public class PackageRepositoriesElementDefaultTests : PackageRepositoriesElementTests + { + protected override bool TestingDefaults + { + get { return true; } + } + + [Test] + public override void Repositories() + { + Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.Count() == 1); + Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).Id == Guid.Parse("65194810-1f85-11dd-bd0b-0800200c9a66")); + Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).Name == "Umbraco package Repository"); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/ProvidersElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/ProvidersElementDefaultTests.cs index c2f6dd4a9c..f85ae7af0c 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/ProvidersElementDefaultTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/ProvidersElementDefaultTests.cs @@ -1,14 +1,14 @@ -using NUnit.Framework; - -namespace Umbraco.Tests.Configurations.UmbracoSettings -{ - [TestFixture] - public class ProvidersElementDefaultTests : ProvidersElementTests - { - protected override bool TestingDefaults - { - get { return true; } - } - - } +using NUnit.Framework; + +namespace Umbraco.Tests.Configurations.UmbracoSettings +{ + [TestFixture] + public class ProvidersElementDefaultTests : ProvidersElementTests + { + protected override bool TestingDefaults + { + get { return true; } + } + + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/RequestHandlerElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/RequestHandlerElementDefaultTests.cs index b05846ccea..92f1cc4957 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/RequestHandlerElementDefaultTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/RequestHandlerElementDefaultTests.cs @@ -1,13 +1,13 @@ -using NUnit.Framework; - -namespace Umbraco.Tests.Configurations.UmbracoSettings -{ - [TestFixture] - public class RequestHandlerElementDefaultTests : RequestHandlerElementTests - { - protected override bool TestingDefaults - { - get { return true; } - } - } +using NUnit.Framework; + +namespace Umbraco.Tests.Configurations.UmbracoSettings +{ + [TestFixture] + public class RequestHandlerElementDefaultTests : RequestHandlerElementTests + { + protected override bool TestingDefaults + { + get { return true; } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/ScriptingElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/ScriptingElementDefaultTests.cs index e51ab0b5a4..69d3c92d38 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/ScriptingElementDefaultTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/ScriptingElementDefaultTests.cs @@ -1,21 +1,21 @@ -using System.Linq; -using NUnit.Framework; - -namespace Umbraco.Tests.Configurations.UmbracoSettings -{ - [TestFixture] - public class ScriptingElementDefaultTests : ScriptingElementTests - { - protected override bool TestingDefaults - { - get { return true; } - } - - [Test] - public override void DataTypeModelStaticMappings() - { - Assert.AreEqual(0, SettingsSection.Scripting.DataTypeModelStaticMappings.Count()); - } - - } +using System.Linq; +using NUnit.Framework; + +namespace Umbraco.Tests.Configurations.UmbracoSettings +{ + [TestFixture] + public class ScriptingElementDefaultTests : ScriptingElementTests + { + protected override bool TestingDefaults + { + get { return true; } + } + + [Test] + public override void DataTypeModelStaticMappings() + { + Assert.AreEqual(0, SettingsSection.Scripting.DataTypeModelStaticMappings.Count()); + } + + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs index 9eb4935fc6..b67abf3e64 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs @@ -1,21 +1,21 @@ -using NUnit.Framework; - -namespace Umbraco.Tests.Configurations.UmbracoSettings -{ - [TestFixture] - public class WebRoutingElementDefaultTests : WebRoutingElementTests - { - - protected override bool TestingDefaults - { - get { return true; } - } - - [Test] - public override void UrlProviderMode() - { - Assert.IsTrue(SettingsSection.WebRouting.UrlProviderMode == "AutoLegacy"); - } - - } +using NUnit.Framework; + +namespace Umbraco.Tests.Configurations.UmbracoSettings +{ + [TestFixture] + public class WebRoutingElementDefaultTests : WebRoutingElementTests + { + + protected override bool TestingDefaults + { + get { return true; } + } + + [Test] + public override void UrlProviderMode() + { + Assert.IsTrue(SettingsSection.WebRouting.UrlProviderMode == "AutoLegacy"); + } + + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Controllers/WebApiEditors/ContentControllerUnitTests.cs b/src/Umbraco.Tests/Controllers/WebApiEditors/ContentControllerUnitTests.cs index 3420a17dc2..687fe05f09 100644 --- a/src/Umbraco.Tests/Controllers/WebApiEditors/ContentControllerUnitTests.cs +++ b/src/Umbraco.Tests/Controllers/WebApiEditors/ContentControllerUnitTests.cs @@ -1,366 +1,366 @@ -using System.Collections.Generic; -using System.Web.Http; -using Moq; -using NUnit.Framework; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Membership; -using Umbraco.Core.Services; -using Umbraco.Web.Editors; - -namespace Umbraco.Tests.Controllers.WebApiEditors -{ - [TestFixture] - public class ContentControllerUnitTests - { - [Test] - public void Access_Allowed_By_Path() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(9); - userMock.Setup(u => u.StartContentId).Returns(-1); - var user = userMock.Object; - var contentMock = new Mock(); - contentMock.Setup(c => c.Path).Returns("-1,1234,5678"); - var content = contentMock.Object; - var contentServiceMock = new Mock(); - contentServiceMock.Setup(x => x.GetById(1234)).Returns(content); - var contentService = contentServiceMock.Object; - - //act - var result = ContentController.CheckPermissions(new Dictionary(), user, null, contentService, 1234); - - //assert - Assert.IsTrue(result); - } - - [Test] - public void Throws_Exception_When_No_Content_Found() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(9); - userMock.Setup(u => u.StartContentId).Returns(-1); - var user = userMock.Object; - var contentMock = new Mock(); - contentMock.Setup(c => c.Path).Returns("-1,1234,5678"); - var content = contentMock.Object; - var contentServiceMock = new Mock(); - contentServiceMock.Setup(x => x.GetById(0)).Returns(content); - var contentService = contentServiceMock.Object; - var userServiceMock = new Mock(); - var permissions = new List(); - userServiceMock.Setup(x => x.GetPermissions(user, 1234)).Returns(permissions); - var userService = userServiceMock.Object; - - //act/assert - Assert.Throws(() => ContentController.CheckPermissions(new Dictionary(), user, userService, contentService, 1234, new[] { 'F' })); - } - - [Test] - public void No_Access_By_Path() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(9); - userMock.Setup(u => u.StartContentId).Returns(9876); - var user = userMock.Object; - var contentMock = new Mock(); - contentMock.Setup(c => c.Path).Returns("-1,1234,5678"); - var content = contentMock.Object; - var contentServiceMock = new Mock(); - contentServiceMock.Setup(x => x.GetById(1234)).Returns(content); - var contentService = contentServiceMock.Object; - var userServiceMock = new Mock(); - var permissions = new List(); - userServiceMock.Setup(x => x.GetPermissions(user, 1234)).Returns(permissions); - var userService = userServiceMock.Object; - - //act - var result = ContentController.CheckPermissions(new Dictionary(), user, userService, contentService, 1234, new[] { 'F'}); - - //assert - Assert.IsFalse(result); - } - - [Test] - public void No_Access_By_Permission() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(9); - userMock.Setup(u => u.StartContentId).Returns(-1); - var user = userMock.Object; - var contentMock = new Mock(); - contentMock.Setup(c => c.Path).Returns("-1,1234,5678"); - var content = contentMock.Object; - var contentServiceMock = new Mock(); - contentServiceMock.Setup(x => x.GetById(1234)).Returns(content); - var contentService = contentServiceMock.Object; - var userServiceMock = new Mock(); - var permissions = new List - { - new EntityPermission(9, 1234, new string[]{ "A", "B", "C" }) - }; - userServiceMock.Setup(x => x.GetPermissions(user, 1234)).Returns(permissions); - var userService = userServiceMock.Object; - - //act - var result = ContentController.CheckPermissions(new Dictionary(), user, userService, contentService, 1234, new[] { 'F'}); - - //assert - Assert.IsFalse(result); - } - - [Test] - public void Access_Allowed_By_Permission() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(9); - userMock.Setup(u => u.StartContentId).Returns(-1); - var user = userMock.Object; - var contentMock = new Mock(); - contentMock.Setup(c => c.Path).Returns("-1,1234,5678"); - var content = contentMock.Object; - var contentServiceMock = new Mock(); - contentServiceMock.Setup(x => x.GetById(1234)).Returns(content); - var contentService = contentServiceMock.Object; - var userServiceMock = new Mock(); - var permissions = new List - { - new EntityPermission(9, 1234, new string[]{ "A", "F", "C" }) - }; - userServiceMock.Setup(x => x.GetPermissions(user, 1234)).Returns(permissions); - var userService = userServiceMock.Object; - - //act - var result = ContentController.CheckPermissions(new Dictionary(), user, userService, contentService, 1234, new[] { 'F'}); - - //assert - Assert.IsTrue(result); - } - - [Test] - public void Access_To_Root_By_Path() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(0); - userMock.Setup(u => u.StartContentId).Returns(-1); - var user = userMock.Object; - - //act - var result = ContentController.CheckPermissions(new Dictionary(), user, null, null, -1); - - //assert - Assert.IsTrue(result); - } - - [Test] - public void Access_To_Recycle_Bin_By_Path() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(0); - userMock.Setup(u => u.StartContentId).Returns(-1); - var user = userMock.Object; - - //act - var result = ContentController.CheckPermissions(new Dictionary(), user, null, null, -20); - - //assert - Assert.IsTrue(result); - } - - [Test] - public void No_Access_To_Recycle_Bin_By_Path() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(0); - userMock.Setup(u => u.StartContentId).Returns(1234); - var user = userMock.Object; - - //act - var result = ContentController.CheckPermissions(new Dictionary(), user, null, null, -20); - - //assert - Assert.IsFalse(result); - } - - [Test] - public void No_Access_To_Root_By_Path() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(0); - userMock.Setup(u => u.StartContentId).Returns(1234); - var user = userMock.Object; - - //act - var result = ContentController.CheckPermissions(new Dictionary(), user, null, null, -1); - - //assert - Assert.IsFalse(result); - } - - [Test] - public void Access_To_Root_By_Permission() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(0); - userMock.Setup(u => u.StartContentId).Returns(-1); - var user = userMock.Object; - - var userServiceMock = new Mock(); - var permissions = new List - { - new EntityPermission(9, 1234, new string[]{ "A" }) - }; - userServiceMock.Setup(x => x.GetPermissions(user, -1)).Returns(permissions); - var userService = userServiceMock.Object; - - //act - var result = ContentController.CheckPermissions(new Dictionary(), user, userService, null, -1, new[] { 'A'}); - - //assert - Assert.IsTrue(result); - } - - [Test] - public void No_Access_To_Root_By_Permission() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(0); - userMock.Setup(u => u.StartContentId).Returns(-1); - var user = userMock.Object; - - var userServiceMock = new Mock(); - var permissions = new List - { - new EntityPermission(9, 1234, new string[]{ "A" }) - }; - userServiceMock.Setup(x => x.GetPermissions(user, -1)).Returns(permissions); - var userService = userServiceMock.Object; - - //act - var result = ContentController.CheckPermissions(new Dictionary(), user, userService, null, -1, new[] { 'B'}); - - //assert - Assert.IsFalse(result); - } - - [Test] - public void Access_To_Recycle_Bin_By_Permission() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(0); - userMock.Setup(u => u.StartContentId).Returns(-1); - var user = userMock.Object; - - var userServiceMock = new Mock(); - var permissions = new List - { - new EntityPermission(9, 1234, new string[]{ "A" }) - }; - userServiceMock.Setup(x => x.GetPermissions(user, -20)).Returns(permissions); - var userService = userServiceMock.Object; - - //act - var result = ContentController.CheckPermissions(new Dictionary(), user, userService, null, -20, new[] { 'A'}); - - //assert - Assert.IsTrue(result); - } - - [Test] - public void No_Access_To_Recycle_Bin_By_Permission() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(0); - userMock.Setup(u => u.StartContentId).Returns(-1); - var user = userMock.Object; - - var userServiceMock = new Mock(); - var permissions = new List - { - new EntityPermission(9, 1234, new string[]{ "A" }) - }; - userServiceMock.Setup(x => x.GetPermissions(user, -20)).Returns(permissions); - var userService = userServiceMock.Object; - - //act - var result = ContentController.CheckPermissions(new Dictionary(), user, userService, null, -20, new[] { 'B'}); - - //assert - Assert.IsFalse(result); - } - - } - - //NOTE: The below self hosted stuff does work so need to get some tests written. Some are not possible atm because - // of the legacy SQL calls like checking permissions. - - //[TestFixture] - //public class ContentControllerHostedTests : BaseRoutingTest - //{ - - // protected override DatabaseBehavior DatabaseTestBehavior - // { - // get { return DatabaseBehavior.NoDatabasePerFixture; } - // } - - // public override void TearDown() - // { - // base.TearDown(); - // UmbracoAuthorizeAttribute.Enable = true; - // UmbracoApplicationAuthorizeAttribute.Enable = true; - // } - - // /// - // /// Tests to ensure that the response filter works so that any items the user - // /// doesn't have access to are removed - // /// - // [Test] - // public async void Get_By_Ids_Response_Filtered() - // { - // UmbracoAuthorizeAttribute.Enable = false; - // UmbracoApplicationAuthorizeAttribute.Enable = false; - - // var baseUrl = string.Format("http://{0}:9876", Environment.MachineName); - // var url = baseUrl + "/api/Content/GetByIds?ids=1&ids=2"; - - // var routingCtx = GetRoutingContext(url, 1234, null, true); - - // var config = new HttpSelfHostConfiguration(baseUrl); - // using (var server = new HttpSelfHostServer(config)) - // { - // var route = config.Routes.MapHttpRoute("test", "api/Content/GetByIds", - // new - // { - // controller = "Content", - // action = "GetByIds", - // id = RouteParameter.Optional - // }); - // route.DataTokens["Namespaces"] = new string[] { "Umbraco.Web.Editors" }; - - // var client = new HttpClient(server); - - // var request = new HttpRequestMessage - // { - // RequestUri = new Uri(url), - // Method = HttpMethod.Get - // }; - - // var result = await client.SendAsync(request); - // } - - // } - - //} -} +using System.Collections.Generic; +using System.Web.Http; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Services; +using Umbraco.Web.Editors; + +namespace Umbraco.Tests.Controllers.WebApiEditors +{ + [TestFixture] + public class ContentControllerUnitTests + { + [Test] + public void Access_Allowed_By_Path() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(9); + userMock.Setup(u => u.StartContentId).Returns(-1); + var user = userMock.Object; + var contentMock = new Mock(); + contentMock.Setup(c => c.Path).Returns("-1,1234,5678"); + var content = contentMock.Object; + var contentServiceMock = new Mock(); + contentServiceMock.Setup(x => x.GetById(1234)).Returns(content); + var contentService = contentServiceMock.Object; + + //act + var result = ContentController.CheckPermissions(new Dictionary(), user, null, contentService, 1234); + + //assert + Assert.IsTrue(result); + } + + [Test] + public void Throws_Exception_When_No_Content_Found() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(9); + userMock.Setup(u => u.StartContentId).Returns(-1); + var user = userMock.Object; + var contentMock = new Mock(); + contentMock.Setup(c => c.Path).Returns("-1,1234,5678"); + var content = contentMock.Object; + var contentServiceMock = new Mock(); + contentServiceMock.Setup(x => x.GetById(0)).Returns(content); + var contentService = contentServiceMock.Object; + var userServiceMock = new Mock(); + var permissions = new List(); + userServiceMock.Setup(x => x.GetPermissions(user, 1234)).Returns(permissions); + var userService = userServiceMock.Object; + + //act/assert + Assert.Throws(() => ContentController.CheckPermissions(new Dictionary(), user, userService, contentService, 1234, new[] { 'F' })); + } + + [Test] + public void No_Access_By_Path() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(9); + userMock.Setup(u => u.StartContentId).Returns(9876); + var user = userMock.Object; + var contentMock = new Mock(); + contentMock.Setup(c => c.Path).Returns("-1,1234,5678"); + var content = contentMock.Object; + var contentServiceMock = new Mock(); + contentServiceMock.Setup(x => x.GetById(1234)).Returns(content); + var contentService = contentServiceMock.Object; + var userServiceMock = new Mock(); + var permissions = new List(); + userServiceMock.Setup(x => x.GetPermissions(user, 1234)).Returns(permissions); + var userService = userServiceMock.Object; + + //act + var result = ContentController.CheckPermissions(new Dictionary(), user, userService, contentService, 1234, new[] { 'F'}); + + //assert + Assert.IsFalse(result); + } + + [Test] + public void No_Access_By_Permission() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(9); + userMock.Setup(u => u.StartContentId).Returns(-1); + var user = userMock.Object; + var contentMock = new Mock(); + contentMock.Setup(c => c.Path).Returns("-1,1234,5678"); + var content = contentMock.Object; + var contentServiceMock = new Mock(); + contentServiceMock.Setup(x => x.GetById(1234)).Returns(content); + var contentService = contentServiceMock.Object; + var userServiceMock = new Mock(); + var permissions = new List + { + new EntityPermission(9, 1234, new string[]{ "A", "B", "C" }) + }; + userServiceMock.Setup(x => x.GetPermissions(user, 1234)).Returns(permissions); + var userService = userServiceMock.Object; + + //act + var result = ContentController.CheckPermissions(new Dictionary(), user, userService, contentService, 1234, new[] { 'F'}); + + //assert + Assert.IsFalse(result); + } + + [Test] + public void Access_Allowed_By_Permission() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(9); + userMock.Setup(u => u.StartContentId).Returns(-1); + var user = userMock.Object; + var contentMock = new Mock(); + contentMock.Setup(c => c.Path).Returns("-1,1234,5678"); + var content = contentMock.Object; + var contentServiceMock = new Mock(); + contentServiceMock.Setup(x => x.GetById(1234)).Returns(content); + var contentService = contentServiceMock.Object; + var userServiceMock = new Mock(); + var permissions = new List + { + new EntityPermission(9, 1234, new string[]{ "A", "F", "C" }) + }; + userServiceMock.Setup(x => x.GetPermissions(user, 1234)).Returns(permissions); + var userService = userServiceMock.Object; + + //act + var result = ContentController.CheckPermissions(new Dictionary(), user, userService, contentService, 1234, new[] { 'F'}); + + //assert + Assert.IsTrue(result); + } + + [Test] + public void Access_To_Root_By_Path() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(0); + userMock.Setup(u => u.StartContentId).Returns(-1); + var user = userMock.Object; + + //act + var result = ContentController.CheckPermissions(new Dictionary(), user, null, null, -1); + + //assert + Assert.IsTrue(result); + } + + [Test] + public void Access_To_Recycle_Bin_By_Path() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(0); + userMock.Setup(u => u.StartContentId).Returns(-1); + var user = userMock.Object; + + //act + var result = ContentController.CheckPermissions(new Dictionary(), user, null, null, -20); + + //assert + Assert.IsTrue(result); + } + + [Test] + public void No_Access_To_Recycle_Bin_By_Path() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(0); + userMock.Setup(u => u.StartContentId).Returns(1234); + var user = userMock.Object; + + //act + var result = ContentController.CheckPermissions(new Dictionary(), user, null, null, -20); + + //assert + Assert.IsFalse(result); + } + + [Test] + public void No_Access_To_Root_By_Path() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(0); + userMock.Setup(u => u.StartContentId).Returns(1234); + var user = userMock.Object; + + //act + var result = ContentController.CheckPermissions(new Dictionary(), user, null, null, -1); + + //assert + Assert.IsFalse(result); + } + + [Test] + public void Access_To_Root_By_Permission() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(0); + userMock.Setup(u => u.StartContentId).Returns(-1); + var user = userMock.Object; + + var userServiceMock = new Mock(); + var permissions = new List + { + new EntityPermission(9, 1234, new string[]{ "A" }) + }; + userServiceMock.Setup(x => x.GetPermissions(user, -1)).Returns(permissions); + var userService = userServiceMock.Object; + + //act + var result = ContentController.CheckPermissions(new Dictionary(), user, userService, null, -1, new[] { 'A'}); + + //assert + Assert.IsTrue(result); + } + + [Test] + public void No_Access_To_Root_By_Permission() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(0); + userMock.Setup(u => u.StartContentId).Returns(-1); + var user = userMock.Object; + + var userServiceMock = new Mock(); + var permissions = new List + { + new EntityPermission(9, 1234, new string[]{ "A" }) + }; + userServiceMock.Setup(x => x.GetPermissions(user, -1)).Returns(permissions); + var userService = userServiceMock.Object; + + //act + var result = ContentController.CheckPermissions(new Dictionary(), user, userService, null, -1, new[] { 'B'}); + + //assert + Assert.IsFalse(result); + } + + [Test] + public void Access_To_Recycle_Bin_By_Permission() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(0); + userMock.Setup(u => u.StartContentId).Returns(-1); + var user = userMock.Object; + + var userServiceMock = new Mock(); + var permissions = new List + { + new EntityPermission(9, 1234, new string[]{ "A" }) + }; + userServiceMock.Setup(x => x.GetPermissions(user, -20)).Returns(permissions); + var userService = userServiceMock.Object; + + //act + var result = ContentController.CheckPermissions(new Dictionary(), user, userService, null, -20, new[] { 'A'}); + + //assert + Assert.IsTrue(result); + } + + [Test] + public void No_Access_To_Recycle_Bin_By_Permission() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(0); + userMock.Setup(u => u.StartContentId).Returns(-1); + var user = userMock.Object; + + var userServiceMock = new Mock(); + var permissions = new List + { + new EntityPermission(9, 1234, new string[]{ "A" }) + }; + userServiceMock.Setup(x => x.GetPermissions(user, -20)).Returns(permissions); + var userService = userServiceMock.Object; + + //act + var result = ContentController.CheckPermissions(new Dictionary(), user, userService, null, -20, new[] { 'B'}); + + //assert + Assert.IsFalse(result); + } + + } + + //NOTE: The below self hosted stuff does work so need to get some tests written. Some are not possible atm because + // of the legacy SQL calls like checking permissions. + + //[TestFixture] + //public class ContentControllerHostedTests : BaseRoutingTest + //{ + + // protected override DatabaseBehavior DatabaseTestBehavior + // { + // get { return DatabaseBehavior.NoDatabasePerFixture; } + // } + + // public override void TearDown() + // { + // base.TearDown(); + // UmbracoAuthorizeAttribute.Enable = true; + // UmbracoApplicationAuthorizeAttribute.Enable = true; + // } + + // /// + // /// Tests to ensure that the response filter works so that any items the user + // /// doesn't have access to are removed + // /// + // [Test] + // public async void Get_By_Ids_Response_Filtered() + // { + // UmbracoAuthorizeAttribute.Enable = false; + // UmbracoApplicationAuthorizeAttribute.Enable = false; + + // var baseUrl = string.Format("http://{0}:9876", Environment.MachineName); + // var url = baseUrl + "/api/Content/GetByIds?ids=1&ids=2"; + + // var routingCtx = GetRoutingContext(url, 1234, null, true); + + // var config = new HttpSelfHostConfiguration(baseUrl); + // using (var server = new HttpSelfHostServer(config)) + // { + // var route = config.Routes.MapHttpRoute("test", "api/Content/GetByIds", + // new + // { + // controller = "Content", + // action = "GetByIds", + // id = RouteParameter.Optional + // }); + // route.DataTokens["Namespaces"] = new string[] { "Umbraco.Web.Editors" }; + + // var client = new HttpClient(server); + + // var request = new HttpRequestMessage + // { + // RequestUri = new Uri(url), + // Method = HttpMethod.Get + // }; + + // var result = await client.SendAsync(request); + // } + + // } + + //} +} diff --git a/src/Umbraco.Tests/Controllers/WebApiEditors/FilterAllowedOutgoingContentAttributeTests.cs b/src/Umbraco.Tests/Controllers/WebApiEditors/FilterAllowedOutgoingContentAttributeTests.cs index c47fd76b30..c0076f17fb 100644 --- a/src/Umbraco.Tests/Controllers/WebApiEditors/FilterAllowedOutgoingContentAttributeTests.cs +++ b/src/Umbraco.Tests/Controllers/WebApiEditors/FilterAllowedOutgoingContentAttributeTests.cs @@ -1,136 +1,136 @@ -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Net.Http.Formatting; -using System.Net.Http.Headers; -using Moq; -using NUnit.Framework; -using Umbraco.Core; -using Umbraco.Core.Models.Membership; -using Umbraco.Core.Services; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.WebApi.Filters; - -namespace Umbraco.Tests.Controllers.WebApiEditors -{ - [TestFixture] - public class FilterAllowedOutgoingContentAttributeTests - { - [Test] - public void GetValueFromResponse_Already_EnumerableContent() - { - var att = new FilterAllowedOutgoingContentAttribute(typeof(IEnumerable)); - var val = new List() {new ContentItemBasic()}; - var result = att.GetValueFromResponse( - new ObjectContent(typeof (IEnumerable), - val, - new JsonMediaTypeFormatter(), - new MediaTypeHeaderValue("html/text"))); - - Assert.AreEqual(val, result); - Assert.AreEqual(1, ((IEnumerable)result).Count()); - } - - [Test] - public void GetValueFromResponse_From_Property() - { - var att = new FilterAllowedOutgoingContentAttribute(typeof(IEnumerable), "MyList"); - var val = new List() { new ContentItemBasic() }; - var container = new MyTestClass() {MyList = val}; - - var result = att.GetValueFromResponse( - new ObjectContent(typeof(MyTestClass), - container, - new JsonMediaTypeFormatter(), - new MediaTypeHeaderValue("html/text"))); - - Assert.AreEqual(val, result); - Assert.AreEqual(1, ((IEnumerable)result).Count()); - } - - [Test] - public void GetValueFromResponse_Returns_Null_Not_Found_Property() - { - var att = new FilterAllowedOutgoingContentAttribute(typeof(IEnumerable), "DontFind"); - var val = new List() { new ContentItemBasic() }; - var container = new MyTestClass() { MyList = val }; - - var result = att.GetValueFromResponse( - new ObjectContent(typeof(MyTestClass), - container, - new JsonMediaTypeFormatter(), - new MediaTypeHeaderValue("html/text"))); - - Assert.AreEqual(null, result); - - } - - [Test] - public void Filter_On_Start_Node() - { - var att = new FilterAllowedOutgoingContentAttribute(typeof(IEnumerable)); - var list = new List(); - var path = ""; - for (var i = 0; i < 10; i++) - { - if (i > 0 && path.EndsWith(",") == false) - { - path += ","; - } - path += i.ToInvariantString(); - list.Add(new ContentItemBasic { Id = i, Name = "Test" + i, ParentId = i, Path = path }); - } - - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(9); - userMock.Setup(u => u.StartContentId).Returns(5); - var user = userMock.Object; - - att.FilterBasedOnStartNode(list, user); - - Assert.AreEqual(5, list.Count); - - } - - [Test] - public void Filter_On_Permissions() - { - var att = new FilterAllowedOutgoingContentAttribute(typeof(IEnumerable)); - var list = new List(); - for (var i = 0; i < 10; i++) - { - list.Add(new ContentItemBasic{Id = i, Name = "Test" + i, ParentId = -1}); - } - var ids = list.Select(x => (int)x.Id).ToArray(); - - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(9); - userMock.Setup(u => u.StartContentId).Returns(-1); - var user = userMock.Object; - - var userServiceMock = new Mock(); - //we're only assigning 3 nodes browse permissions so that is what we expect as a result - var permissions = new List - { - new EntityPermission(9, 1, new string[]{ "F" }), - new EntityPermission(9, 2, new string[]{ "F" }), - new EntityPermission(9, 3, new string[]{ "F" }), - new EntityPermission(9, 4, new string[]{ "A" }) - }; - userServiceMock.Setup(x => x.GetPermissions(user, ids)).Returns(permissions); - var userService = userServiceMock.Object; - - att.FilterBasedOnPermissions(list, user, userService); - - Assert.AreEqual(3, list.Count); - Assert.AreEqual(1, list.ElementAt(0).Id); - Assert.AreEqual(2, list.ElementAt(1).Id); - Assert.AreEqual(3, list.ElementAt(2).Id); - } - - private class MyTestClass - { - public IEnumerable MyList { get; set; } - } - } +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Formatting; +using System.Net.Http.Headers; +using Moq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Services; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.WebApi.Filters; + +namespace Umbraco.Tests.Controllers.WebApiEditors +{ + [TestFixture] + public class FilterAllowedOutgoingContentAttributeTests + { + [Test] + public void GetValueFromResponse_Already_EnumerableContent() + { + var att = new FilterAllowedOutgoingContentAttribute(typeof(IEnumerable)); + var val = new List() {new ContentItemBasic()}; + var result = att.GetValueFromResponse( + new ObjectContent(typeof (IEnumerable), + val, + new JsonMediaTypeFormatter(), + new MediaTypeHeaderValue("html/text"))); + + Assert.AreEqual(val, result); + Assert.AreEqual(1, ((IEnumerable)result).Count()); + } + + [Test] + public void GetValueFromResponse_From_Property() + { + var att = new FilterAllowedOutgoingContentAttribute(typeof(IEnumerable), "MyList"); + var val = new List() { new ContentItemBasic() }; + var container = new MyTestClass() {MyList = val}; + + var result = att.GetValueFromResponse( + new ObjectContent(typeof(MyTestClass), + container, + new JsonMediaTypeFormatter(), + new MediaTypeHeaderValue("html/text"))); + + Assert.AreEqual(val, result); + Assert.AreEqual(1, ((IEnumerable)result).Count()); + } + + [Test] + public void GetValueFromResponse_Returns_Null_Not_Found_Property() + { + var att = new FilterAllowedOutgoingContentAttribute(typeof(IEnumerable), "DontFind"); + var val = new List() { new ContentItemBasic() }; + var container = new MyTestClass() { MyList = val }; + + var result = att.GetValueFromResponse( + new ObjectContent(typeof(MyTestClass), + container, + new JsonMediaTypeFormatter(), + new MediaTypeHeaderValue("html/text"))); + + Assert.AreEqual(null, result); + + } + + [Test] + public void Filter_On_Start_Node() + { + var att = new FilterAllowedOutgoingContentAttribute(typeof(IEnumerable)); + var list = new List(); + var path = ""; + for (var i = 0; i < 10; i++) + { + if (i > 0 && path.EndsWith(",") == false) + { + path += ","; + } + path += i.ToInvariantString(); + list.Add(new ContentItemBasic { Id = i, Name = "Test" + i, ParentId = i, Path = path }); + } + + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(9); + userMock.Setup(u => u.StartContentId).Returns(5); + var user = userMock.Object; + + att.FilterBasedOnStartNode(list, user); + + Assert.AreEqual(5, list.Count); + + } + + [Test] + public void Filter_On_Permissions() + { + var att = new FilterAllowedOutgoingContentAttribute(typeof(IEnumerable)); + var list = new List(); + for (var i = 0; i < 10; i++) + { + list.Add(new ContentItemBasic{Id = i, Name = "Test" + i, ParentId = -1}); + } + var ids = list.Select(x => (int)x.Id).ToArray(); + + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(9); + userMock.Setup(u => u.StartContentId).Returns(-1); + var user = userMock.Object; + + var userServiceMock = new Mock(); + //we're only assigning 3 nodes browse permissions so that is what we expect as a result + var permissions = new List + { + new EntityPermission(9, 1, new string[]{ "F" }), + new EntityPermission(9, 2, new string[]{ "F" }), + new EntityPermission(9, 3, new string[]{ "F" }), + new EntityPermission(9, 4, new string[]{ "A" }) + }; + userServiceMock.Setup(x => x.GetPermissions(user, ids)).Returns(permissions); + var userService = userServiceMock.Object; + + att.FilterBasedOnPermissions(list, user, userService); + + Assert.AreEqual(3, list.Count); + Assert.AreEqual(1, list.ElementAt(0).Id); + Assert.AreEqual(2, list.ElementAt(1).Id); + Assert.AreEqual(3, list.ElementAt(2).Id); + } + + private class MyTestClass + { + public IEnumerable MyList { get; set; } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Controllers/WebApiEditors/MediaControllerUnitTests.cs b/src/Umbraco.Tests/Controllers/WebApiEditors/MediaControllerUnitTests.cs index c24b5059a2..c426b35b9c 100644 --- a/src/Umbraco.Tests/Controllers/WebApiEditors/MediaControllerUnitTests.cs +++ b/src/Umbraco.Tests/Controllers/WebApiEditors/MediaControllerUnitTests.cs @@ -1,142 +1,142 @@ -using System.Collections.Generic; -using System.Web.Http; -using Moq; -using NUnit.Framework; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Membership; -using Umbraco.Core.Services; -using Umbraco.Web.Editors; - -namespace Umbraco.Tests.Controllers.WebApiEditors -{ - [TestFixture] - public class MediaControllerUnitTests - { - [Test] - public void Access_Allowed_By_Path() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(9); - userMock.Setup(u => u.StartMediaId).Returns(-1); - var user = userMock.Object; - var mediaMock = new Mock(); - mediaMock.Setup(m => m.Path).Returns("-1,1234,5678"); - var media = mediaMock.Object; - var mediaServiceMock = new Mock(); - mediaServiceMock.Setup(x => x.GetById(1234)).Returns(media); - var mediaService = mediaServiceMock.Object; - - //act - var result = MediaController.CheckPermissions(new Dictionary(), user, mediaService, 1234); - - //assert - Assert.IsTrue(result); - } - - [Test] - public void Throws_Exception_When_No_Media_Found() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(9); - userMock.Setup(u => u.StartMediaId).Returns(-1); - var user = userMock.Object; - var mediaMock = new Mock(); - mediaMock.Setup(m => m.Path).Returns("-1,1234,5678"); - var media = mediaMock.Object; - var mediaServiceMock = new Mock(); - mediaServiceMock.Setup(x => x.GetById(0)).Returns(media); - var mediaService = mediaServiceMock.Object; - - //act/assert - Assert.Throws(() => MediaController.CheckPermissions(new Dictionary(), user, mediaService, 1234)); - } - - [Test] - public void No_Access_By_Path() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(9); - userMock.Setup(u => u.StartMediaId).Returns(9876); - var user = userMock.Object; - var mediaMock = new Mock(); - mediaMock.Setup(m => m.Path).Returns("-1,1234,5678"); - var media = mediaMock.Object; - var mediaServiceMock = new Mock(); - mediaServiceMock.Setup(x => x.GetById(1234)).Returns(media); - var mediaService = mediaServiceMock.Object; - - //act - var result = MediaController.CheckPermissions(new Dictionary(), user, mediaService, 1234); - - //assert - Assert.IsFalse(result); - } - - [Test] - public void Access_To_Root_By_Path() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(0); - userMock.Setup(u => u.StartMediaId).Returns(-1); - var user = userMock.Object; - - //act - var result = MediaController.CheckPermissions(new Dictionary(), user, null, -1); - - //assert - Assert.IsTrue(result); - } - - [Test] - public void No_Access_To_Root_By_Path() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(0); - userMock.Setup(u => u.StartMediaId).Returns(1234); - var user = userMock.Object; - - //act - var result = MediaController.CheckPermissions(new Dictionary(), user, null, -1); - - //assert - Assert.IsFalse(result); - } - - [Test] - public void Access_To_Recycle_Bin_By_Path() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(0); - userMock.Setup(u => u.StartMediaId).Returns(-1); - var user = userMock.Object; - - //act - var result = MediaController.CheckPermissions(new Dictionary(), user, null, -21); - - //assert - Assert.IsTrue(result); - } - - [Test] - public void No_Access_To_Recycle_Bin_By_Path() - { - //arrange - var userMock = new Mock(); - userMock.Setup(u => u.Id).Returns(0); - userMock.Setup(u => u.StartMediaId).Returns(1234); - var user = userMock.Object; - - //act - var result = MediaController.CheckPermissions(new Dictionary(), user, null, -21); - - //assert - Assert.IsFalse(result); - } - } +using System.Collections.Generic; +using System.Web.Http; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Services; +using Umbraco.Web.Editors; + +namespace Umbraco.Tests.Controllers.WebApiEditors +{ + [TestFixture] + public class MediaControllerUnitTests + { + [Test] + public void Access_Allowed_By_Path() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(9); + userMock.Setup(u => u.StartMediaId).Returns(-1); + var user = userMock.Object; + var mediaMock = new Mock(); + mediaMock.Setup(m => m.Path).Returns("-1,1234,5678"); + var media = mediaMock.Object; + var mediaServiceMock = new Mock(); + mediaServiceMock.Setup(x => x.GetById(1234)).Returns(media); + var mediaService = mediaServiceMock.Object; + + //act + var result = MediaController.CheckPermissions(new Dictionary(), user, mediaService, 1234); + + //assert + Assert.IsTrue(result); + } + + [Test] + public void Throws_Exception_When_No_Media_Found() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(9); + userMock.Setup(u => u.StartMediaId).Returns(-1); + var user = userMock.Object; + var mediaMock = new Mock(); + mediaMock.Setup(m => m.Path).Returns("-1,1234,5678"); + var media = mediaMock.Object; + var mediaServiceMock = new Mock(); + mediaServiceMock.Setup(x => x.GetById(0)).Returns(media); + var mediaService = mediaServiceMock.Object; + + //act/assert + Assert.Throws(() => MediaController.CheckPermissions(new Dictionary(), user, mediaService, 1234)); + } + + [Test] + public void No_Access_By_Path() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(9); + userMock.Setup(u => u.StartMediaId).Returns(9876); + var user = userMock.Object; + var mediaMock = new Mock(); + mediaMock.Setup(m => m.Path).Returns("-1,1234,5678"); + var media = mediaMock.Object; + var mediaServiceMock = new Mock(); + mediaServiceMock.Setup(x => x.GetById(1234)).Returns(media); + var mediaService = mediaServiceMock.Object; + + //act + var result = MediaController.CheckPermissions(new Dictionary(), user, mediaService, 1234); + + //assert + Assert.IsFalse(result); + } + + [Test] + public void Access_To_Root_By_Path() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(0); + userMock.Setup(u => u.StartMediaId).Returns(-1); + var user = userMock.Object; + + //act + var result = MediaController.CheckPermissions(new Dictionary(), user, null, -1); + + //assert + Assert.IsTrue(result); + } + + [Test] + public void No_Access_To_Root_By_Path() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(0); + userMock.Setup(u => u.StartMediaId).Returns(1234); + var user = userMock.Object; + + //act + var result = MediaController.CheckPermissions(new Dictionary(), user, null, -1); + + //assert + Assert.IsFalse(result); + } + + [Test] + public void Access_To_Recycle_Bin_By_Path() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(0); + userMock.Setup(u => u.StartMediaId).Returns(-1); + var user = userMock.Object; + + //act + var result = MediaController.CheckPermissions(new Dictionary(), user, null, -21); + + //assert + Assert.IsTrue(result); + } + + [Test] + public void No_Access_To_Recycle_Bin_By_Path() + { + //arrange + var userMock = new Mock(); + userMock.Setup(u => u.Id).Returns(0); + userMock.Setup(u => u.StartMediaId).Returns(1234); + var user = userMock.Object; + + //act + var result = MediaController.CheckPermissions(new Dictionary(), user, null, -21); + + //assert + Assert.IsFalse(result); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/CoreXml/NavigableNavigatorTests.cs b/src/Umbraco.Tests/CoreXml/NavigableNavigatorTests.cs index a26cf0fcb2..0f4b4a4f9e 100644 --- a/src/Umbraco.Tests/CoreXml/NavigableNavigatorTests.cs +++ b/src/Umbraco.Tests/CoreXml/NavigableNavigatorTests.cs @@ -1,1174 +1,1174 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; -using System.Text; -using System.Xml; -using System.Xml.XPath; -using System.Xml.Xsl; -using umbraco; -using Umbraco.Core; -using Umbraco.Core.Xml; -using Umbraco.Core.Xml.XPath; -using NUnit.Framework; - -namespace Umbraco.Tests.CoreXml -{ - [TestFixture] - public class NavigableNavigatorTests - { - [Test] - public void NewNavigatorIsAtRoot() - { - const string xml = @""; - var doc = XmlHelper.CreateXPathDocument(xml); - var nav = doc.CreateNavigator(); - - Assert.AreEqual(XPathNodeType.Root, nav.NodeType); - Assert.AreEqual(string.Empty, nav.Name); - - var source = new TestSource5(); - nav = new NavigableNavigator(source); - - Assert.AreEqual(XPathNodeType.Root, nav.NodeType); - Assert.AreEqual(string.Empty, nav.Name); - } - - [Test] - public void NativeXmlValues() - { - const string xml = @" - - - - - - - - blah - - blah - - - bam - - - -"; - var doc = XmlHelper.CreateXPathDocument(xml); - var nav = doc.CreateNavigator(); - - NavigatorValues(nav, true); - } - - [Test] - public void NavigableXmlValues() - { - var source = new TestSource6(); - var nav = new NavigableNavigator(source); - - NavigatorValues(nav, false); - } - - static void NavigatorValues(XPathNavigator nav, bool native) - { - // in non-native we can't have Value dump everything, else - // we'd dump the entire database? Makes not much sense. - - Assert.AreEqual(native ? "\n blah\n blah\n bam\n " : string.Empty, nav.Value.Lf()); // !! - Assert.IsTrue(nav.MoveToFirstChild()); - Assert.AreEqual("root", nav.Name); - Assert.AreEqual(native ? "\n blah\n blah\n bam\n " : string.Empty, nav.Value.Lf()); // !! - Assert.IsTrue(nav.MoveToFirstChild()); - Assert.AreEqual("wrap", nav.Name); - Assert.AreEqual(native ? "\n blah\n blah\n bam\n " : string.Empty, nav.Value.Lf()); // !! - - Assert.IsTrue(nav.MoveToFirstChild()); - Assert.AreEqual("item1", nav.Name); - Assert.AreEqual(string.Empty, nav.Value); - Assert.IsFalse(nav.MoveToFirstChild()); - - Assert.IsTrue(nav.MoveToNext()); - Assert.AreEqual("item2", nav.Name); - Assert.AreEqual(string.Empty, nav.Value); - Assert.IsFalse(nav.MoveToFirstChild()); // !! - - Assert.IsTrue(nav.MoveToNext()); - Assert.AreEqual("item2a", nav.Name); - Assert.AreEqual(string.Empty, nav.Value); - Assert.IsFalse(nav.MoveToFirstChild()); // !! - - Assert.IsTrue(nav.MoveToNext()); - Assert.AreEqual("item2b", nav.Name); - Assert.AreEqual(string.Empty, nav.Value); - Assert.IsFalse(nav.MoveToFirstChild()); // !! - - // we have no way to tell the navigable that a value is CDATA - // so the rule is, if it's null it's not there, anything else is there - // and the filtering has to be done when building the content - - Assert.IsTrue(nav.MoveToNext()); - Assert.AreEqual("item2c", nav.Name); - Assert.AreEqual("\n ", nav.Value.Lf()); // ok since it's a property - Assert.IsTrue(nav.MoveToFirstChild()); - Assert.AreEqual(XPathNodeType.Text, nav.NodeType); - Assert.AreEqual(string.Empty, nav.Name); - Assert.AreEqual("\n ", nav.Value.Lf()); - Assert.IsTrue(nav.MoveToParent()); - - Assert.IsTrue(nav.MoveToNext()); - Assert.AreEqual("item3", nav.Name); - Assert.AreEqual("blah", nav.Value); // ok since it's a property - Assert.IsTrue(nav.MoveToFirstChild()); - Assert.AreEqual(XPathNodeType.Text, nav.NodeType); - Assert.AreEqual(string.Empty, nav.Name); - Assert.AreEqual("blah", nav.Value); - Assert.IsTrue(nav.MoveToParent()); - - Assert.IsTrue(nav.MoveToNext()); - Assert.AreEqual("item3a", nav.Name); - Assert.AreEqual("\n blah\n ", nav.Value.Lf()); // ok since it's a property - Assert.IsTrue(nav.MoveToFirstChild()); - Assert.AreEqual(XPathNodeType.Text, nav.NodeType); - Assert.AreEqual(string.Empty, nav.Name); - Assert.AreEqual("\n blah\n ", nav.Value.Lf()); - Assert.IsTrue(nav.MoveToParent()); - - Assert.IsTrue(nav.MoveToNext()); - Assert.AreEqual("item4", nav.Name); - Assert.AreEqual("bam", nav.Value); // ok since it's a property - Assert.IsTrue(nav.MoveToFirstChild()); - Assert.AreEqual("subitem", nav.Name); - Assert.AreEqual("bam", nav.Value); // ok since we're in a fragment - Assert.IsTrue(nav.MoveToFirstChild()); - Assert.AreEqual(XPathNodeType.Text, nav.NodeType); - Assert.AreEqual(string.Empty, nav.Name); - Assert.AreEqual("bam", nav.Value); - Assert.IsFalse(nav.MoveToNext()); - Assert.IsTrue(nav.MoveToParent()); - Assert.AreEqual("subitem", nav.Name); - Assert.IsFalse(nav.MoveToNext()); - Assert.IsTrue(nav.MoveToParent()); - Assert.AreEqual("item4", nav.Name); - - Assert.IsTrue(nav.MoveToNext()); - Assert.AreEqual("item5", nav.Name); - Assert.AreEqual("\n ", nav.Value.Lf()); - Assert.IsTrue(nav.MoveToFirstChild()); - Assert.AreEqual(XPathNodeType.Text, nav.NodeType); - Assert.AreEqual("\n ", nav.Value.Lf()); - } - - [Test] - public void Navigate() - { - var source = new TestSource1(); - var nav = new NavigableNavigator(source); - - nav.MoveToRoot(); - Assert.AreEqual("", nav.Name); // because we're at root - nav.MoveToFirstChild(); - Assert.AreEqual("root", nav.Name); - nav.MoveToFirstChild(); - Assert.AreEqual("type1", nav.Name); // our first content - nav.MoveToFirstAttribute(); - Assert.AreEqual("id", nav.Name); - Assert.AreEqual("1", nav.Value); - nav.MoveToNextAttribute(); - Assert.AreEqual("prop1", nav.Name); - Assert.AreEqual("1:p1", nav.Value); - nav.MoveToNextAttribute(); - Assert.AreEqual("prop2", nav.Name); - Assert.AreEqual("1:p2", nav.Value); - Assert.IsFalse(nav.MoveToNextAttribute()); - nav.MoveToParent(); - nav.MoveToFirstChild(); - Assert.AreEqual("prop3", nav.Name); - Assert.AreEqual("1:p3", nav.Value); - - Assert.IsFalse(nav.MoveToNext()); - } - - [Test] - public void NavigateMixed() - { - var source = new TestSource2(); - var nav = new NavigableNavigator(source); - - nav.MoveToRoot(); - nav.MoveToFirstChild(); - Assert.AreEqual("root", nav.Name); - nav.MoveToFirstChild(); - Assert.AreEqual("type1", nav.Name); // our content - nav.MoveToFirstChild(); - Assert.AreEqual("prop1", nav.Name); // our property - Assert.AreEqual(XPathNodeType.Element, nav.NodeType); - nav.MoveToFirstChild(); - - // "poo" - - Assert.AreEqual(XPathNodeType.Element, nav.NodeType); - Assert.AreEqual("data", nav.Name); - - nav.MoveToFirstChild(); - Assert.AreEqual(XPathNodeType.Element, nav.NodeType); - Assert.AreEqual("item1", nav.Name); - - nav.MoveToNext(); - Assert.AreEqual(XPathNodeType.Element, nav.NodeType); - Assert.AreEqual("item2", nav.Name); - - nav.MoveToParent(); - Assert.AreEqual(XPathNodeType.Element, nav.NodeType); - Assert.AreEqual("data", nav.Name); - - nav.MoveToParent(); - Assert.AreEqual(XPathNodeType.Element, nav.NodeType); - Assert.AreEqual("prop1", nav.Name); - } - - [Test] - public void OuterXmlBasic() - { - const string xml = @""; - - var doc = XmlHelper.CreateXPathDocument(xml); - var nnav = doc.CreateNavigator(); - Assert.AreEqual(xml, nnav.OuterXml); - - var source = new TestSource0(); - var nav = new NavigableNavigator(source); - Assert.AreEqual(xml, nav.OuterXml); - } - - [Test] - public void OuterXml() - { - var source = new TestSource1(); - var nav = new NavigableNavigator(source); - - const string xml = @" - - 1:p3 - -"; - - Assert.AreEqual(xml.Lf(), nav.OuterXml.Lf()); - } - - [Test] - public void OuterXmlMixed() - { - var source = new TestSource2(); - var nav = new NavigableNavigator(source); - - nav.MoveToRoot(); - - const string outerXml = @" - - - - poo - - - - - -"; - - Assert.AreEqual(outerXml.Lf(), nav.OuterXml.Lf()); - } - - [Test] - public void Query() - { - var source = new TestSource1(); - var nav = new NavigableNavigator(source); - - var iterator = nav.Select("//type1"); - Assert.AreEqual(1, iterator.Count); - iterator.MoveNext(); - Assert.AreEqual("type1", iterator.Current.Name); - - iterator = nav.Select("//* [@prop1='1:p1']"); - Assert.AreEqual(1, iterator.Count); - iterator.MoveNext(); - Assert.AreEqual("type1", iterator.Current.Name); - } - - [Test] - public void QueryMixed() - { - var source = new TestSource2(); - var nav = new NavigableNavigator(source); - - var doc = XmlHelper.CreateXPathDocument("poo"); - var docNav = doc.CreateNavigator(); - var docIter = docNav.Select("//item2 [@xx=33]"); - Assert.AreEqual(1, docIter.Count); - Assert.AreEqual("", docIter.Current.Name); - docIter.MoveNext(); - Assert.AreEqual("item2", docIter.Current.Name); - - var iterator = nav.Select("//item2 [@xx=33]"); - Assert.AreEqual(1, iterator.Count); - Assert.AreEqual("", iterator.Current.Name); - iterator.MoveNext(); - Assert.AreEqual("item2", iterator.Current.Name); - } - - [Test] - public void QueryWithVariables() - { - var source = new TestSource1(); - var nav = new NavigableNavigator(source); - - var iterator = nav.Select("//* [@prop1=$var]", new XPathVariable("var", "1:p1")); - Assert.AreEqual(1, iterator.Count); - iterator.MoveNext(); - Assert.AreEqual("type1", iterator.Current.Name); - } - - [Test] - public void QueryMixedWithVariables() - { - var source = new TestSource2(); - var nav = new NavigableNavigator(source); - - var iterator = nav.Select("//item2 [@xx=$var]", new XPathVariable("var", "33")); - Assert.AreEqual(1, iterator.Count); - iterator.MoveNext(); - Assert.AreEqual("item2", iterator.Current.Name); - } - - [Test] - public void MixedWithNoValue() - { - var source = new TestSource4(); - var nav = new NavigableNavigator(source); - - var doc = XmlHelper.CreateXPathDocument(@" - dang - - - "); - var docNav = doc.CreateNavigator(); - - docNav.MoveToRoot(); - Assert.IsTrue(docNav.MoveToFirstChild()); - Assert.AreEqual("root", docNav.Name); - Assert.IsTrue(docNav.MoveToFirstChild()); - Assert.AreEqual("type1", docNav.Name); - Assert.IsTrue(docNav.MoveToNext()); - Assert.AreEqual("type1", docNav.Name); - Assert.IsTrue(docNav.MoveToNext()); - Assert.AreEqual("type1", docNav.Name); - Assert.IsFalse(docNav.MoveToNext()); - - docNav.MoveToRoot(); - var docOuter = docNav.OuterXml; - - nav.MoveToRoot(); - Assert.IsTrue(nav.MoveToFirstChild()); - Assert.AreEqual("root", nav.Name); - Assert.IsTrue(nav.MoveToFirstChild()); - Assert.AreEqual("type1", nav.Name); - Assert.IsTrue(nav.MoveToNext()); - Assert.AreEqual("type1", nav.Name); - Assert.IsTrue(nav.MoveToNext()); - Assert.AreEqual("type1", nav.Name); - Assert.IsFalse(nav.MoveToNext()); - - nav.MoveToRoot(); - var outer = nav.OuterXml; - - Assert.AreEqual(docOuter, outer); - } - - [Test] - [Ignore("NavigableNavigator does not implement IHasXmlNode.")] - public void XmlNodeList() - { - var source = new TestSource1(); - var nav = new NavigableNavigator(source); - - var iterator = nav.Select("/*"); - - // but, that requires that the underlying navigator implements IHasXmlNode - // so it is possible to obtain nodes from the navigator - not possible yet - var nodes = XmlNodeListFactory.CreateNodeList(iterator); - - Assert.AreEqual(nodes.Count, 1); - var node = nodes[0]; - - Assert.AreEqual(3, node.Attributes.Count); - Assert.AreEqual("1", node.Attributes["id"].Value); - Assert.AreEqual("1:p1", node.Attributes["prop1"].Value); - Assert.AreEqual("1:p2", node.Attributes["prop2"].Value); - Assert.AreEqual(1, node.ChildNodes.Count); - Assert.AreEqual("prop3", node.FirstChild.Name); - Assert.AreEqual("1:p3", node.FirstChild.Value); - } - - [Test] - public void CloneIsSafe() - { - var source = new TestSource5(); - var nav = new NavigableNavigator(source); - TestContent content; - - Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position); - Assert.IsTrue(nav.MoveToFirstChild()); - Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); - Assert.AreEqual("root", nav.Name); // at -1 - Assert.IsTrue(nav.MoveToFirstChild()); - Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); - Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id); - Assert.IsTrue(nav.MoveToFirstChild()); - Assert.AreEqual(NavigableNavigator.StatePosition.PropertyElement, nav.InternalState.Position); - Assert.AreEqual("prop1", nav.Name); - Assert.IsTrue(nav.MoveToNext()); - Assert.AreEqual(NavigableNavigator.StatePosition.PropertyElement, nav.InternalState.Position); - Assert.AreEqual("prop2", nav.Name); - Assert.IsTrue(nav.MoveToNext()); - Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); - Assert.AreEqual(3, (nav.UnderlyingObject as TestContent).Id); - - // at that point nav is at /root/1/3 - - var clone = nav.Clone() as NavigableNavigator; - - // move nav to /root/1/5 and ensure that clone stays at /root/1/3 - Assert.IsTrue(nav.MoveToNext()); - Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); - Assert.AreEqual(5, (nav.UnderlyingObject as TestContent).Id); - Assert.AreEqual(NavigableNavigator.StatePosition.Element, clone.InternalState.Position); - Assert.AreEqual(3, (clone.UnderlyingObject as TestContent).Id); - - // move nav to /root/2 - Assert.IsTrue(nav.MoveToParent()); - Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); - Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id); - Assert.IsTrue(nav.MoveToNext()); - Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); - Assert.AreEqual(2, (nav.UnderlyingObject as TestContent).Id); - Assert.IsTrue(nav.MoveToFirstChild()); - Assert.AreEqual(NavigableNavigator.StatePosition.PropertyElement, nav.InternalState.Position); - Assert.AreEqual("prop1", nav.Name); - Assert.AreEqual("p21", nav.Value); - - // move clone to .. /root/1 - Assert.IsTrue(clone.MoveToParent()); - - // clone has not been corrupted by nav - Assert.AreEqual(NavigableNavigator.StatePosition.Element, clone.InternalState.Position); - Assert.AreEqual(1, (clone.UnderlyingObject as TestContent).Id); - } - - [TestCase(1)] - [TestCase(2)] - [TestCase(3)] - [TestCase(4)] - [TestCase(5)] - [TestCase(6)] - public void SelectById(int id) - { - var source = new TestSource5(); - var nav = new NavigableNavigator(source); - - var iter = nav.Select(string.Format("//* [@id={0}]", id)); - Assert.IsTrue(iter.MoveNext()); - var current = iter.Current as NavigableNavigator; - Assert.AreEqual(NavigableNavigator.StatePosition.Element, current.InternalState.Position); - Assert.AreEqual(id, (current.UnderlyingObject as TestContent).Id); - } - - [TestCase(1)] - [TestCase(2)] - [TestCase(3)] - [TestCase(4)] - [TestCase(5)] - [TestCase(6)] - public void SelectByIdWithVariable(int id) - { - var source = new TestSource5(); - var nav = new NavigableNavigator(source); - - var iter = nav.Select("//* [@id=$id]", new XPathVariable("id", id.ToString(CultureInfo.InvariantCulture))); - Assert.IsTrue(iter.MoveNext()); - var current = iter.Current as NavigableNavigator; - Assert.AreEqual(NavigableNavigator.StatePosition.Element, current.InternalState.Position); - Assert.AreEqual(id, (current.UnderlyingObject as TestContent).Id); - } - - [Test] - public void MoveToId() - { - var source = new TestSource5(); - var nav = new NavigableNavigator(source); - - // move to /root/1/3 - Assert.IsTrue(nav.MoveToId("3")); - Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); - Assert.AreEqual(3, (nav.UnderlyingObject as TestContent).Id); - - // move to /root/1 - Assert.IsTrue(nav.MoveToParent()); - Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); - Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id); - - // move to /root - Assert.IsTrue(nav.MoveToParent()); - Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); - Assert.AreEqual(-1, (nav.UnderlyingObject as TestContent).Id); - - // move up - Assert.IsTrue(nav.MoveToParent()); - Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position); - Assert.IsFalse(nav.MoveToParent()); - - // move to /root/1 - Assert.IsTrue(nav.MoveToId("1")); - Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); - Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id); - - // move to /root - Assert.IsTrue(nav.MoveToParent()); - Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); - Assert.AreEqual(-1, (nav.UnderlyingObject as TestContent).Id); - - // move up - Assert.IsTrue(nav.MoveToParent()); - Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position); - Assert.IsFalse(nav.MoveToParent()); - - // move to /root - Assert.IsTrue(nav.MoveToId("-1")); - Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); - Assert.AreEqual(-1, (nav.UnderlyingObject as TestContent).Id); - - // move up - Assert.IsTrue(nav.MoveToParent()); - Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position); - Assert.IsFalse(nav.MoveToParent()); - - // get lost - Assert.IsFalse(nav.MoveToId("666")); - } - - [Test] - public void RootedNavigator() - { - var source = new TestSource5(); - var nav = new NavigableNavigator(source, source.Get(1)); - - // go to (/root) /1 - Assert.IsTrue(nav.MoveToFirstChild()); - Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); - Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id); - - // go to (/root) /1/prop1 - Assert.IsTrue(nav.MoveToFirstChild()); - // go to (/root) /1/prop2 - Assert.IsTrue(nav.MoveToNext()); - // go to (/root) /1/3 - Assert.IsTrue(nav.MoveToNext()); - Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); - Assert.AreEqual(3, (nav.UnderlyingObject as TestContent).Id); - - // go to (/root) /1 - Assert.IsTrue(nav.MoveToParent()); - Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); - Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id); - - // go to (/root) ie root - Assert.IsTrue(nav.MoveToParent()); - Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position); - Assert.IsFalse(nav.MoveToParent()); - - // can't go there - Assert.IsFalse(nav.MoveToId("2")); - } - - [TestCase(true, true)] - [TestCase(true, false)] - [TestCase(false, true)] - [TestCase(false, false)] - public void XsltDebugModeAndSortOrder(bool native, bool debug) - { - const string xml = @" - - - title-1 - - title-3 - - title-7 - - - title-8 - - - - title-5 - - - - title-2 - - title-4 - - - title-6 - - - -"; - - const string xslt = @" - -]> - - - - - - - - - -! - - -!! - - -!!! - - - - - - - -"; - const string expected = @"! title-1 -!! title-3 -!!! title-7 -!!! title-8 -!! title-5 -! title-2 -!! title-4 -!! title-6 -"; - - // see http://www.onenaught.com/posts/352/xslt-performance-tip-dont-indent-output - // why aren't we using an XmlWriter here? - - var transform = new XslCompiledTransform(debug); - var xmlReader = new XmlTextReader(new StringReader(xslt)) - { - EntityHandling = EntityHandling.ExpandEntities - }; - var xslResolver = new XmlUrlResolver - { - Credentials = CredentialCache.DefaultCredentials - }; - var args = new XsltArgumentList(); - - // .Default is more restrictive than .TrustedXslt - transform.Load(xmlReader, XsltSettings.Default, xslResolver); - - XPathNavigator macro; - if (!native) - { - var source = new TestSource7(); - var nav = new NavigableNavigator(source); - //args.AddParam("currentPage", string.Empty, nav.Clone()); - - var x = new XmlDocument(); - x.LoadXml(xml); - - macro = new MacroNavigator(new[] - { - // it even fails like that => macro nav. issue? - new MacroNavigator.MacroParameter("nav", x.CreateNavigator()) // nav.Clone()) - } - ); - } - else - { - var doc = new XmlDocument(); - doc.LoadXml(""); - var nav = doc.CreateElement("nav"); - doc.DocumentElement.AppendChild(nav); - var x = new XmlDocument(); - x.LoadXml(xml); - nav.AppendChild(doc.ImportNode(x.DocumentElement, true)); - macro = doc.CreateNavigator(); - } - - var writer = new StringWriter(); - transform.Transform(macro, args, writer); - - // this was working with native, debug and non-debug - // this was working with macro nav, non-debug - // but was NOT working (changing the order of nodes) with macro nav, debug - // was due to an issue with macro nav IsSamePosition, fixed - - //Console.WriteLine("--------"); - //Console.WriteLine(writer.ToString()); - Assert.AreEqual(expected.Lf(), writer.ToString().Lf()); - } - - [Test] - public void WhiteSpacesAndEmptyValues() - { - - // "When Microsoft’s DOM builder receives a text node from the parser - // that contains only white space, it is thrown away." - so if it's ONLY - // spaces, it's nothing, but spaces are NOT trimmed. - - // For attributes, spaces are preserved even when there's only spaces. - - var doc = XmlHelper.CreateXPathDocument(@" - - - - - - ooo - ooo - - - - "); - - var docNav = doc.CreateNavigator(); - - Assert.AreEqual(@" - - - - - - - - - - - - - - ooo - - - ooo - - - - -".Lf(), docNav.OuterXml.Lf()); - - docNav.MoveToRoot(); - Assert.IsTrue(docNav.MoveToFirstChild()); - Assert.IsTrue(docNav.MoveToFirstChild()); - Assert.IsTrue(docNav.MoveToFirstChild()); // prop - Assert.IsTrue(docNav.IsEmptyElement); - Assert.IsTrue(docNav.MoveToParent()); - Assert.IsTrue(docNav.MoveToNext()); - Assert.IsTrue(docNav.MoveToFirstChild()); // prop - Assert.IsFalse(docNav.IsEmptyElement); - Assert.AreEqual("", docNav.Value); // contains an empty text node - Assert.IsTrue(docNav.MoveToParent()); - Assert.IsTrue(docNav.MoveToNext()); - Assert.IsTrue(docNav.MoveToFirstChild()); // prop - Assert.IsFalse(docNav.IsEmptyElement); - Assert.AreEqual("", docNav.Value); // contains an empty text node - - var source = new TestSource8(); - var nav = new NavigableNavigator(source); - - // shows how whitespaces are handled by NavigableNavigator - Assert.AreEqual(@" - - - - - - - - - - - - - - - ooo - -".Lf(), nav.OuterXml.Lf()); - } - } - - #region Navigable implementation - - class TestPropertyType : INavigableFieldType - { - public TestPropertyType(string name, bool isXmlContent = false, Func xmlStringConverter = null) - { - Name = name; - IsXmlContent = isXmlContent; - XmlStringConverter = xmlStringConverter; - } - - public string Name { get; private set; } - public bool IsXmlContent { get; private set; } - public Func XmlStringConverter { get; private set; } - } - - class TestContentType : INavigableContentType - { - public TestContentType(TestSourceBase source, string name, params INavigableFieldType[] properties) - { - Source = source; - Name = name; - FieldTypes = properties; - } - - public TestSourceBase Source { get; private set; } - public string Name { get; private set; } - public INavigableFieldType[] FieldTypes { get; protected set; } - } - - class TestRootContentType : TestContentType - { - public TestRootContentType(TestSourceBase source, params INavigableFieldType[] properties) - : base(source, "root") - { - FieldTypes = properties; - } - - public TestContentType CreateType(string name, params INavigableFieldType[] properties) - { - return new TestContentType(Source, name, FieldTypes.Union(properties).ToArray()); - } - } - - class TestContent : INavigableContent - { - public TestContent(TestContentType type, int id, int parentId) - { - _type = type; - Id = id; - ParentId = parentId; - } - - private readonly TestContentType _type; - public int Id { get; private set; } - public int ParentId { get; private set; } - public INavigableContentType Type { get { return _type; } } - public IList ChildIds { get; private set; } - - public object Value(int id) - { - var fieldType = _type.FieldTypes[id] as TestPropertyType; - if (fieldType == null) throw new Exception("Oops"); - - var value = FieldValues[id]; - var isAttr = id <= _type.Source.LastAttributeIndex; - - // null => return null - if (value == null) return null; - - // attribute => return string value - if (isAttr) return value.ToString(); - - // has a converter => use the converter - if (fieldType.XmlStringConverter != null) - return fieldType.XmlStringConverter(value); - - // not a string => return value as a string - var s = value as string; - if (s == null) return value.ToString(); - - // xml content... try xml - if (fieldType.IsXmlContent) - { - XPathDocument doc; - if (XmlHelper.TryCreateXPathDocumentFromPropertyValue(s, out doc)) - return doc.CreateNavigator(); - } - - // return the string - // even if it's xml that can't be parsed... - return s; - } - - // locals - public object[] FieldValues { get; private set; } - - public TestContent WithChildren(params int[] childIds) - { - ChildIds = childIds; - return this; - } - - public TestContent WithValues(params object[] values) - { - FieldValues = values == null ? new object[] {null} : values; - return this; - } - } - - class TestRootContent : TestContent - { - public TestRootContent(TestContentType type) - : base(type, -1, -1) - { } - } - - abstract class TestSourceBase : INavigableSource - { - protected readonly Dictionary Content = new Dictionary(); - - public INavigableContent Get(int id) - { - return Content.ContainsKey(id) ? Content[id] : null; - } - - public int LastAttributeIndex { get; protected set; } - - public INavigableContent Root { get; protected set; } - } - - #endregion - - #region Navigable sources - - class TestSource0 : TestSourceBase - { - public TestSource0() - { - LastAttributeIndex = -1; - var type = new TestRootContentType(this); - Root = new TestRootContent(type); - } - } - - class TestSource1 : TestSourceBase - { - public TestSource1() - { - // last attribute index is 1 - meaning properties 0 and 1 are attributes, 2+ are elements - // then, fieldValues must have adequate number of items - LastAttributeIndex = 1; - - var prop1 = new TestPropertyType("prop1"); - var prop2 = new TestPropertyType("prop2"); - var prop3 = new TestPropertyType("prop3"); - var type = new TestRootContentType(this, prop1, prop2); - var type1 = type.CreateType("type1", prop3); - - Content[1] = new TestContent(type1, 1, -1).WithValues("1:p1", "1:p2", "1:p3"); - - Root = new TestRootContent(type).WithValues("", "").WithChildren(1); - } - } - - class TestSource2 : TestSourceBase - { - public TestSource2() - { - LastAttributeIndex = -1; - - var prop1 = new TestPropertyType("prop1", true); - var type = new TestRootContentType(this); - var type1 = type.CreateType("type1", prop1); - - const string xml = "poo"; - Content[1] = new TestContent(type1, 1, 1).WithValues(xml); - - Root = new TestRootContent(type).WithChildren(1); - } - } - - class TestSource3 : TestSourceBase - { - public TestSource3() - { - LastAttributeIndex = 1; - - var prop1 = new TestPropertyType("prop1"); - var prop2 = new TestPropertyType("prop2"); - var prop3 = new TestPropertyType("prop3"); - var type = new TestRootContentType(this, prop1, prop2); - var type1 = type.CreateType("type1", prop3); - - Content[1] = new TestContent(type1, 1, 1).WithValues("1:p1", "1:p2", "1:p3").WithChildren(2); - Content[2] = new TestContent(type1, 2, 1).WithValues("2:p1", "2:p2", "2:p3"); - - Root = new TestRootContent(type).WithChildren(1); - } - } - - class TestSource4 : TestSourceBase - { - public TestSource4() - { - LastAttributeIndex = -1; - - var prop1 = new TestPropertyType("prop1", true); - var prop2 = new TestPropertyType("prop2"); - var type = new TestRootContentType(this); - var type1 = type.CreateType("type1", prop1, prop2); - - Content[1] = new TestContent(type1, 1, -1).WithValues("", "dang"); - Content[2] = new TestContent(type1, 2, -1).WithValues(null, ""); - Content[3] = new TestContent(type1, 3, -1).WithValues(null, null); - - Root = new TestRootContent(type).WithChildren(1, 2, 3); - } - } - - class TestSource5 : TestSourceBase - { - public TestSource5() - { - LastAttributeIndex = -1; - - var prop1 = new TestPropertyType("prop1"); - var prop2 = new TestPropertyType("prop2"); - var type = new TestRootContentType(this); - var type1 = type.CreateType("type1", prop1, prop2); - - Content[1] = new TestContent(type1, 1, -1).WithValues("p11", "p12").WithChildren(3, 5); - Content[2] = new TestContent(type1, 2, -1).WithValues("p21", "p22").WithChildren(4, 6); - Content[3] = new TestContent(type1, 3, 1).WithValues("p31", "p32"); - Content[4] = new TestContent(type1, 4, 2).WithValues("p41", "p42"); - Content[5] = new TestContent(type1, 5, 1).WithValues("p51", "p52"); - Content[6] = new TestContent(type1, 6, 2).WithValues("p61", "p62"); - - Root = new TestRootContent(type).WithChildren(1, 2); - } - } - - class TestSource6 : TestSourceBase - { - // - // - // - // - // - // - // - // - // blah - // - // bam - // - // - // - // - // - - public TestSource6() - { - LastAttributeIndex = -1; - - var type = new TestRootContentType(this); - var type1 = type.CreateType("wrap", - new TestPropertyType("item1"), - new TestPropertyType("item2"), - new TestPropertyType("item2a"), - new TestPropertyType("item2b"), - new TestPropertyType("item2c"), - new TestPropertyType("item3"), - new TestPropertyType("item3a"), - new TestPropertyType("item4", true), - new TestPropertyType("item5", true) - ); - - Content[1] = new TestContent(type1, 1, -1) - .WithValues( - null, - null, - null, - null, - "\n ", - "blah", - "\n blah\n ", - "bam", - "\n " - ); - - Root = new TestRootContent(type).WithChildren(1); - } - } - - class TestSource7 : TestSourceBase - { - public TestSource7() - { - LastAttributeIndex = 0; - - var prop1 = new TestPropertyType("isDoc"); - var prop2 = new TestPropertyType("title"); - var type = new TestRootContentType(this, prop1); - var type1 = type.CreateType("node", prop1, prop2); - - Content[1] = new TestContent(type1, 1, -1).WithValues(1, "title-1").WithChildren(3, 5); - Content[2] = new TestContent(type1, 2, -1).WithValues(1, "title-2").WithChildren(4, 6); - Content[3] = new TestContent(type1, 3, 1).WithValues(1, "title-3").WithChildren(7, 8); - Content[4] = new TestContent(type1, 4, 2).WithValues(1, "title-4"); - Content[5] = new TestContent(type1, 5, 1).WithValues(1, "title-5"); - Content[6] = new TestContent(type1, 6, 2).WithValues(1, "title-6"); - - Content[7] = new TestContent(type1, 7, 3).WithValues(1, "title-7"); - Content[8] = new TestContent(type1, 8, 3).WithValues(1, "title-8"); - - Root = new TestRootContent(type).WithValues(null).WithChildren(1, 2); - } - } - - class TestSource8 : TestSourceBase - { - public TestSource8() - { - LastAttributeIndex = 0; - - var attr = new TestPropertyType("attr"); - var prop = new TestPropertyType("prop"); - var type = new TestRootContentType(this, attr); - var type1 = type.CreateType("item", attr, prop); - Content[1] = new TestContent(type1, 1, -1).WithValues(null, null); - Content[2] = new TestContent(type1, 2, -1).WithValues("", ""); - Content[3] = new TestContent(type1, 3, -1).WithValues(" ", " "); - Content[4] = new TestContent(type1, 4, -1).WithValues("", "\n"); - Content[5] = new TestContent(type1, 5, -1).WithValues(" ooo ", " ooo "); - Root = new TestRootContent(type).WithValues(null).WithChildren(1, 2, 3, 4, 5); - } - } - - #endregion - - static class StringCrLfExtensions - { - public static string Lf(this string s) - { - if (string.IsNullOrEmpty(s)) return s; - return s.Replace("\r", ""); // remove Cr - } - } +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Xml; +using System.Xml.XPath; +using System.Xml.Xsl; +using umbraco; +using Umbraco.Core; +using Umbraco.Core.Xml; +using Umbraco.Core.Xml.XPath; +using NUnit.Framework; + +namespace Umbraco.Tests.CoreXml +{ + [TestFixture] + public class NavigableNavigatorTests + { + [Test] + public void NewNavigatorIsAtRoot() + { + const string xml = @""; + var doc = XmlHelper.CreateXPathDocument(xml); + var nav = doc.CreateNavigator(); + + Assert.AreEqual(XPathNodeType.Root, nav.NodeType); + Assert.AreEqual(string.Empty, nav.Name); + + var source = new TestSource5(); + nav = new NavigableNavigator(source); + + Assert.AreEqual(XPathNodeType.Root, nav.NodeType); + Assert.AreEqual(string.Empty, nav.Name); + } + + [Test] + public void NativeXmlValues() + { + const string xml = @" + + + + + + + + blah + + blah + + + bam + + + +"; + var doc = XmlHelper.CreateXPathDocument(xml); + var nav = doc.CreateNavigator(); + + NavigatorValues(nav, true); + } + + [Test] + public void NavigableXmlValues() + { + var source = new TestSource6(); + var nav = new NavigableNavigator(source); + + NavigatorValues(nav, false); + } + + static void NavigatorValues(XPathNavigator nav, bool native) + { + // in non-native we can't have Value dump everything, else + // we'd dump the entire database? Makes not much sense. + + Assert.AreEqual(native ? "\n blah\n blah\n bam\n " : string.Empty, nav.Value.Lf()); // !! + Assert.IsTrue(nav.MoveToFirstChild()); + Assert.AreEqual("root", nav.Name); + Assert.AreEqual(native ? "\n blah\n blah\n bam\n " : string.Empty, nav.Value.Lf()); // !! + Assert.IsTrue(nav.MoveToFirstChild()); + Assert.AreEqual("wrap", nav.Name); + Assert.AreEqual(native ? "\n blah\n blah\n bam\n " : string.Empty, nav.Value.Lf()); // !! + + Assert.IsTrue(nav.MoveToFirstChild()); + Assert.AreEqual("item1", nav.Name); + Assert.AreEqual(string.Empty, nav.Value); + Assert.IsFalse(nav.MoveToFirstChild()); + + Assert.IsTrue(nav.MoveToNext()); + Assert.AreEqual("item2", nav.Name); + Assert.AreEqual(string.Empty, nav.Value); + Assert.IsFalse(nav.MoveToFirstChild()); // !! + + Assert.IsTrue(nav.MoveToNext()); + Assert.AreEqual("item2a", nav.Name); + Assert.AreEqual(string.Empty, nav.Value); + Assert.IsFalse(nav.MoveToFirstChild()); // !! + + Assert.IsTrue(nav.MoveToNext()); + Assert.AreEqual("item2b", nav.Name); + Assert.AreEqual(string.Empty, nav.Value); + Assert.IsFalse(nav.MoveToFirstChild()); // !! + + // we have no way to tell the navigable that a value is CDATA + // so the rule is, if it's null it's not there, anything else is there + // and the filtering has to be done when building the content + + Assert.IsTrue(nav.MoveToNext()); + Assert.AreEqual("item2c", nav.Name); + Assert.AreEqual("\n ", nav.Value.Lf()); // ok since it's a property + Assert.IsTrue(nav.MoveToFirstChild()); + Assert.AreEqual(XPathNodeType.Text, nav.NodeType); + Assert.AreEqual(string.Empty, nav.Name); + Assert.AreEqual("\n ", nav.Value.Lf()); + Assert.IsTrue(nav.MoveToParent()); + + Assert.IsTrue(nav.MoveToNext()); + Assert.AreEqual("item3", nav.Name); + Assert.AreEqual("blah", nav.Value); // ok since it's a property + Assert.IsTrue(nav.MoveToFirstChild()); + Assert.AreEqual(XPathNodeType.Text, nav.NodeType); + Assert.AreEqual(string.Empty, nav.Name); + Assert.AreEqual("blah", nav.Value); + Assert.IsTrue(nav.MoveToParent()); + + Assert.IsTrue(nav.MoveToNext()); + Assert.AreEqual("item3a", nav.Name); + Assert.AreEqual("\n blah\n ", nav.Value.Lf()); // ok since it's a property + Assert.IsTrue(nav.MoveToFirstChild()); + Assert.AreEqual(XPathNodeType.Text, nav.NodeType); + Assert.AreEqual(string.Empty, nav.Name); + Assert.AreEqual("\n blah\n ", nav.Value.Lf()); + Assert.IsTrue(nav.MoveToParent()); + + Assert.IsTrue(nav.MoveToNext()); + Assert.AreEqual("item4", nav.Name); + Assert.AreEqual("bam", nav.Value); // ok since it's a property + Assert.IsTrue(nav.MoveToFirstChild()); + Assert.AreEqual("subitem", nav.Name); + Assert.AreEqual("bam", nav.Value); // ok since we're in a fragment + Assert.IsTrue(nav.MoveToFirstChild()); + Assert.AreEqual(XPathNodeType.Text, nav.NodeType); + Assert.AreEqual(string.Empty, nav.Name); + Assert.AreEqual("bam", nav.Value); + Assert.IsFalse(nav.MoveToNext()); + Assert.IsTrue(nav.MoveToParent()); + Assert.AreEqual("subitem", nav.Name); + Assert.IsFalse(nav.MoveToNext()); + Assert.IsTrue(nav.MoveToParent()); + Assert.AreEqual("item4", nav.Name); + + Assert.IsTrue(nav.MoveToNext()); + Assert.AreEqual("item5", nav.Name); + Assert.AreEqual("\n ", nav.Value.Lf()); + Assert.IsTrue(nav.MoveToFirstChild()); + Assert.AreEqual(XPathNodeType.Text, nav.NodeType); + Assert.AreEqual("\n ", nav.Value.Lf()); + } + + [Test] + public void Navigate() + { + var source = new TestSource1(); + var nav = new NavigableNavigator(source); + + nav.MoveToRoot(); + Assert.AreEqual("", nav.Name); // because we're at root + nav.MoveToFirstChild(); + Assert.AreEqual("root", nav.Name); + nav.MoveToFirstChild(); + Assert.AreEqual("type1", nav.Name); // our first content + nav.MoveToFirstAttribute(); + Assert.AreEqual("id", nav.Name); + Assert.AreEqual("1", nav.Value); + nav.MoveToNextAttribute(); + Assert.AreEqual("prop1", nav.Name); + Assert.AreEqual("1:p1", nav.Value); + nav.MoveToNextAttribute(); + Assert.AreEqual("prop2", nav.Name); + Assert.AreEqual("1:p2", nav.Value); + Assert.IsFalse(nav.MoveToNextAttribute()); + nav.MoveToParent(); + nav.MoveToFirstChild(); + Assert.AreEqual("prop3", nav.Name); + Assert.AreEqual("1:p3", nav.Value); + + Assert.IsFalse(nav.MoveToNext()); + } + + [Test] + public void NavigateMixed() + { + var source = new TestSource2(); + var nav = new NavigableNavigator(source); + + nav.MoveToRoot(); + nav.MoveToFirstChild(); + Assert.AreEqual("root", nav.Name); + nav.MoveToFirstChild(); + Assert.AreEqual("type1", nav.Name); // our content + nav.MoveToFirstChild(); + Assert.AreEqual("prop1", nav.Name); // our property + Assert.AreEqual(XPathNodeType.Element, nav.NodeType); + nav.MoveToFirstChild(); + + // "poo" + + Assert.AreEqual(XPathNodeType.Element, nav.NodeType); + Assert.AreEqual("data", nav.Name); + + nav.MoveToFirstChild(); + Assert.AreEqual(XPathNodeType.Element, nav.NodeType); + Assert.AreEqual("item1", nav.Name); + + nav.MoveToNext(); + Assert.AreEqual(XPathNodeType.Element, nav.NodeType); + Assert.AreEqual("item2", nav.Name); + + nav.MoveToParent(); + Assert.AreEqual(XPathNodeType.Element, nav.NodeType); + Assert.AreEqual("data", nav.Name); + + nav.MoveToParent(); + Assert.AreEqual(XPathNodeType.Element, nav.NodeType); + Assert.AreEqual("prop1", nav.Name); + } + + [Test] + public void OuterXmlBasic() + { + const string xml = @""; + + var doc = XmlHelper.CreateXPathDocument(xml); + var nnav = doc.CreateNavigator(); + Assert.AreEqual(xml, nnav.OuterXml); + + var source = new TestSource0(); + var nav = new NavigableNavigator(source); + Assert.AreEqual(xml, nav.OuterXml); + } + + [Test] + public void OuterXml() + { + var source = new TestSource1(); + var nav = new NavigableNavigator(source); + + const string xml = @" + + 1:p3 + +"; + + Assert.AreEqual(xml.Lf(), nav.OuterXml.Lf()); + } + + [Test] + public void OuterXmlMixed() + { + var source = new TestSource2(); + var nav = new NavigableNavigator(source); + + nav.MoveToRoot(); + + const string outerXml = @" + + + + poo + + + + + +"; + + Assert.AreEqual(outerXml.Lf(), nav.OuterXml.Lf()); + } + + [Test] + public void Query() + { + var source = new TestSource1(); + var nav = new NavigableNavigator(source); + + var iterator = nav.Select("//type1"); + Assert.AreEqual(1, iterator.Count); + iterator.MoveNext(); + Assert.AreEqual("type1", iterator.Current.Name); + + iterator = nav.Select("//* [@prop1='1:p1']"); + Assert.AreEqual(1, iterator.Count); + iterator.MoveNext(); + Assert.AreEqual("type1", iterator.Current.Name); + } + + [Test] + public void QueryMixed() + { + var source = new TestSource2(); + var nav = new NavigableNavigator(source); + + var doc = XmlHelper.CreateXPathDocument("poo"); + var docNav = doc.CreateNavigator(); + var docIter = docNav.Select("//item2 [@xx=33]"); + Assert.AreEqual(1, docIter.Count); + Assert.AreEqual("", docIter.Current.Name); + docIter.MoveNext(); + Assert.AreEqual("item2", docIter.Current.Name); + + var iterator = nav.Select("//item2 [@xx=33]"); + Assert.AreEqual(1, iterator.Count); + Assert.AreEqual("", iterator.Current.Name); + iterator.MoveNext(); + Assert.AreEqual("item2", iterator.Current.Name); + } + + [Test] + public void QueryWithVariables() + { + var source = new TestSource1(); + var nav = new NavigableNavigator(source); + + var iterator = nav.Select("//* [@prop1=$var]", new XPathVariable("var", "1:p1")); + Assert.AreEqual(1, iterator.Count); + iterator.MoveNext(); + Assert.AreEqual("type1", iterator.Current.Name); + } + + [Test] + public void QueryMixedWithVariables() + { + var source = new TestSource2(); + var nav = new NavigableNavigator(source); + + var iterator = nav.Select("//item2 [@xx=$var]", new XPathVariable("var", "33")); + Assert.AreEqual(1, iterator.Count); + iterator.MoveNext(); + Assert.AreEqual("item2", iterator.Current.Name); + } + + [Test] + public void MixedWithNoValue() + { + var source = new TestSource4(); + var nav = new NavigableNavigator(source); + + var doc = XmlHelper.CreateXPathDocument(@" + dang + + + "); + var docNav = doc.CreateNavigator(); + + docNav.MoveToRoot(); + Assert.IsTrue(docNav.MoveToFirstChild()); + Assert.AreEqual("root", docNav.Name); + Assert.IsTrue(docNav.MoveToFirstChild()); + Assert.AreEqual("type1", docNav.Name); + Assert.IsTrue(docNav.MoveToNext()); + Assert.AreEqual("type1", docNav.Name); + Assert.IsTrue(docNav.MoveToNext()); + Assert.AreEqual("type1", docNav.Name); + Assert.IsFalse(docNav.MoveToNext()); + + docNav.MoveToRoot(); + var docOuter = docNav.OuterXml; + + nav.MoveToRoot(); + Assert.IsTrue(nav.MoveToFirstChild()); + Assert.AreEqual("root", nav.Name); + Assert.IsTrue(nav.MoveToFirstChild()); + Assert.AreEqual("type1", nav.Name); + Assert.IsTrue(nav.MoveToNext()); + Assert.AreEqual("type1", nav.Name); + Assert.IsTrue(nav.MoveToNext()); + Assert.AreEqual("type1", nav.Name); + Assert.IsFalse(nav.MoveToNext()); + + nav.MoveToRoot(); + var outer = nav.OuterXml; + + Assert.AreEqual(docOuter, outer); + } + + [Test] + [Ignore("NavigableNavigator does not implement IHasXmlNode.")] + public void XmlNodeList() + { + var source = new TestSource1(); + var nav = new NavigableNavigator(source); + + var iterator = nav.Select("/*"); + + // but, that requires that the underlying navigator implements IHasXmlNode + // so it is possible to obtain nodes from the navigator - not possible yet + var nodes = XmlNodeListFactory.CreateNodeList(iterator); + + Assert.AreEqual(nodes.Count, 1); + var node = nodes[0]; + + Assert.AreEqual(3, node.Attributes.Count); + Assert.AreEqual("1", node.Attributes["id"].Value); + Assert.AreEqual("1:p1", node.Attributes["prop1"].Value); + Assert.AreEqual("1:p2", node.Attributes["prop2"].Value); + Assert.AreEqual(1, node.ChildNodes.Count); + Assert.AreEqual("prop3", node.FirstChild.Name); + Assert.AreEqual("1:p3", node.FirstChild.Value); + } + + [Test] + public void CloneIsSafe() + { + var source = new TestSource5(); + var nav = new NavigableNavigator(source); + TestContent content; + + Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position); + Assert.IsTrue(nav.MoveToFirstChild()); + Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); + Assert.AreEqual("root", nav.Name); // at -1 + Assert.IsTrue(nav.MoveToFirstChild()); + Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); + Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id); + Assert.IsTrue(nav.MoveToFirstChild()); + Assert.AreEqual(NavigableNavigator.StatePosition.PropertyElement, nav.InternalState.Position); + Assert.AreEqual("prop1", nav.Name); + Assert.IsTrue(nav.MoveToNext()); + Assert.AreEqual(NavigableNavigator.StatePosition.PropertyElement, nav.InternalState.Position); + Assert.AreEqual("prop2", nav.Name); + Assert.IsTrue(nav.MoveToNext()); + Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); + Assert.AreEqual(3, (nav.UnderlyingObject as TestContent).Id); + + // at that point nav is at /root/1/3 + + var clone = nav.Clone() as NavigableNavigator; + + // move nav to /root/1/5 and ensure that clone stays at /root/1/3 + Assert.IsTrue(nav.MoveToNext()); + Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); + Assert.AreEqual(5, (nav.UnderlyingObject as TestContent).Id); + Assert.AreEqual(NavigableNavigator.StatePosition.Element, clone.InternalState.Position); + Assert.AreEqual(3, (clone.UnderlyingObject as TestContent).Id); + + // move nav to /root/2 + Assert.IsTrue(nav.MoveToParent()); + Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); + Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id); + Assert.IsTrue(nav.MoveToNext()); + Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); + Assert.AreEqual(2, (nav.UnderlyingObject as TestContent).Id); + Assert.IsTrue(nav.MoveToFirstChild()); + Assert.AreEqual(NavigableNavigator.StatePosition.PropertyElement, nav.InternalState.Position); + Assert.AreEqual("prop1", nav.Name); + Assert.AreEqual("p21", nav.Value); + + // move clone to .. /root/1 + Assert.IsTrue(clone.MoveToParent()); + + // clone has not been corrupted by nav + Assert.AreEqual(NavigableNavigator.StatePosition.Element, clone.InternalState.Position); + Assert.AreEqual(1, (clone.UnderlyingObject as TestContent).Id); + } + + [TestCase(1)] + [TestCase(2)] + [TestCase(3)] + [TestCase(4)] + [TestCase(5)] + [TestCase(6)] + public void SelectById(int id) + { + var source = new TestSource5(); + var nav = new NavigableNavigator(source); + + var iter = nav.Select(string.Format("//* [@id={0}]", id)); + Assert.IsTrue(iter.MoveNext()); + var current = iter.Current as NavigableNavigator; + Assert.AreEqual(NavigableNavigator.StatePosition.Element, current.InternalState.Position); + Assert.AreEqual(id, (current.UnderlyingObject as TestContent).Id); + } + + [TestCase(1)] + [TestCase(2)] + [TestCase(3)] + [TestCase(4)] + [TestCase(5)] + [TestCase(6)] + public void SelectByIdWithVariable(int id) + { + var source = new TestSource5(); + var nav = new NavigableNavigator(source); + + var iter = nav.Select("//* [@id=$id]", new XPathVariable("id", id.ToString(CultureInfo.InvariantCulture))); + Assert.IsTrue(iter.MoveNext()); + var current = iter.Current as NavigableNavigator; + Assert.AreEqual(NavigableNavigator.StatePosition.Element, current.InternalState.Position); + Assert.AreEqual(id, (current.UnderlyingObject as TestContent).Id); + } + + [Test] + public void MoveToId() + { + var source = new TestSource5(); + var nav = new NavigableNavigator(source); + + // move to /root/1/3 + Assert.IsTrue(nav.MoveToId("3")); + Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); + Assert.AreEqual(3, (nav.UnderlyingObject as TestContent).Id); + + // move to /root/1 + Assert.IsTrue(nav.MoveToParent()); + Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); + Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id); + + // move to /root + Assert.IsTrue(nav.MoveToParent()); + Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); + Assert.AreEqual(-1, (nav.UnderlyingObject as TestContent).Id); + + // move up + Assert.IsTrue(nav.MoveToParent()); + Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position); + Assert.IsFalse(nav.MoveToParent()); + + // move to /root/1 + Assert.IsTrue(nav.MoveToId("1")); + Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); + Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id); + + // move to /root + Assert.IsTrue(nav.MoveToParent()); + Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); + Assert.AreEqual(-1, (nav.UnderlyingObject as TestContent).Id); + + // move up + Assert.IsTrue(nav.MoveToParent()); + Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position); + Assert.IsFalse(nav.MoveToParent()); + + // move to /root + Assert.IsTrue(nav.MoveToId("-1")); + Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); + Assert.AreEqual(-1, (nav.UnderlyingObject as TestContent).Id); + + // move up + Assert.IsTrue(nav.MoveToParent()); + Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position); + Assert.IsFalse(nav.MoveToParent()); + + // get lost + Assert.IsFalse(nav.MoveToId("666")); + } + + [Test] + public void RootedNavigator() + { + var source = new TestSource5(); + var nav = new NavigableNavigator(source, source.Get(1)); + + // go to (/root) /1 + Assert.IsTrue(nav.MoveToFirstChild()); + Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); + Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id); + + // go to (/root) /1/prop1 + Assert.IsTrue(nav.MoveToFirstChild()); + // go to (/root) /1/prop2 + Assert.IsTrue(nav.MoveToNext()); + // go to (/root) /1/3 + Assert.IsTrue(nav.MoveToNext()); + Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); + Assert.AreEqual(3, (nav.UnderlyingObject as TestContent).Id); + + // go to (/root) /1 + Assert.IsTrue(nav.MoveToParent()); + Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position); + Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id); + + // go to (/root) ie root + Assert.IsTrue(nav.MoveToParent()); + Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position); + Assert.IsFalse(nav.MoveToParent()); + + // can't go there + Assert.IsFalse(nav.MoveToId("2")); + } + + [TestCase(true, true)] + [TestCase(true, false)] + [TestCase(false, true)] + [TestCase(false, false)] + public void XsltDebugModeAndSortOrder(bool native, bool debug) + { + const string xml = @" + + + title-1 + + title-3 + + title-7 + + + title-8 + + + + title-5 + + + + title-2 + + title-4 + + + title-6 + + + +"; + + const string xslt = @" + +]> + + + + + + + + + +! + + +!! + + +!!! + + + + + + + +"; + const string expected = @"! title-1 +!! title-3 +!!! title-7 +!!! title-8 +!! title-5 +! title-2 +!! title-4 +!! title-6 +"; + + // see http://www.onenaught.com/posts/352/xslt-performance-tip-dont-indent-output + // why aren't we using an XmlWriter here? + + var transform = new XslCompiledTransform(debug); + var xmlReader = new XmlTextReader(new StringReader(xslt)) + { + EntityHandling = EntityHandling.ExpandEntities + }; + var xslResolver = new XmlUrlResolver + { + Credentials = CredentialCache.DefaultCredentials + }; + var args = new XsltArgumentList(); + + // .Default is more restrictive than .TrustedXslt + transform.Load(xmlReader, XsltSettings.Default, xslResolver); + + XPathNavigator macro; + if (!native) + { + var source = new TestSource7(); + var nav = new NavigableNavigator(source); + //args.AddParam("currentPage", string.Empty, nav.Clone()); + + var x = new XmlDocument(); + x.LoadXml(xml); + + macro = new MacroNavigator(new[] + { + // it even fails like that => macro nav. issue? + new MacroNavigator.MacroParameter("nav", x.CreateNavigator()) // nav.Clone()) + } + ); + } + else + { + var doc = new XmlDocument(); + doc.LoadXml(""); + var nav = doc.CreateElement("nav"); + doc.DocumentElement.AppendChild(nav); + var x = new XmlDocument(); + x.LoadXml(xml); + nav.AppendChild(doc.ImportNode(x.DocumentElement, true)); + macro = doc.CreateNavigator(); + } + + var writer = new StringWriter(); + transform.Transform(macro, args, writer); + + // this was working with native, debug and non-debug + // this was working with macro nav, non-debug + // but was NOT working (changing the order of nodes) with macro nav, debug + // was due to an issue with macro nav IsSamePosition, fixed + + //Console.WriteLine("--------"); + //Console.WriteLine(writer.ToString()); + Assert.AreEqual(expected.Lf(), writer.ToString().Lf()); + } + + [Test] + public void WhiteSpacesAndEmptyValues() + { + + // "When Microsoft’s DOM builder receives a text node from the parser + // that contains only white space, it is thrown away." - so if it's ONLY + // spaces, it's nothing, but spaces are NOT trimmed. + + // For attributes, spaces are preserved even when there's only spaces. + + var doc = XmlHelper.CreateXPathDocument(@" + + + + + + ooo + ooo + + + + "); + + var docNav = doc.CreateNavigator(); + + Assert.AreEqual(@" + + + + + + + + + + + + + + ooo + + + ooo + + + + +".Lf(), docNav.OuterXml.Lf()); + + docNav.MoveToRoot(); + Assert.IsTrue(docNav.MoveToFirstChild()); + Assert.IsTrue(docNav.MoveToFirstChild()); + Assert.IsTrue(docNav.MoveToFirstChild()); // prop + Assert.IsTrue(docNav.IsEmptyElement); + Assert.IsTrue(docNav.MoveToParent()); + Assert.IsTrue(docNav.MoveToNext()); + Assert.IsTrue(docNav.MoveToFirstChild()); // prop + Assert.IsFalse(docNav.IsEmptyElement); + Assert.AreEqual("", docNav.Value); // contains an empty text node + Assert.IsTrue(docNav.MoveToParent()); + Assert.IsTrue(docNav.MoveToNext()); + Assert.IsTrue(docNav.MoveToFirstChild()); // prop + Assert.IsFalse(docNav.IsEmptyElement); + Assert.AreEqual("", docNav.Value); // contains an empty text node + + var source = new TestSource8(); + var nav = new NavigableNavigator(source); + + // shows how whitespaces are handled by NavigableNavigator + Assert.AreEqual(@" + + + + + + + + + + + + + + + ooo + +".Lf(), nav.OuterXml.Lf()); + } + } + + #region Navigable implementation + + class TestPropertyType : INavigableFieldType + { + public TestPropertyType(string name, bool isXmlContent = false, Func xmlStringConverter = null) + { + Name = name; + IsXmlContent = isXmlContent; + XmlStringConverter = xmlStringConverter; + } + + public string Name { get; private set; } + public bool IsXmlContent { get; private set; } + public Func XmlStringConverter { get; private set; } + } + + class TestContentType : INavigableContentType + { + public TestContentType(TestSourceBase source, string name, params INavigableFieldType[] properties) + { + Source = source; + Name = name; + FieldTypes = properties; + } + + public TestSourceBase Source { get; private set; } + public string Name { get; private set; } + public INavigableFieldType[] FieldTypes { get; protected set; } + } + + class TestRootContentType : TestContentType + { + public TestRootContentType(TestSourceBase source, params INavigableFieldType[] properties) + : base(source, "root") + { + FieldTypes = properties; + } + + public TestContentType CreateType(string name, params INavigableFieldType[] properties) + { + return new TestContentType(Source, name, FieldTypes.Union(properties).ToArray()); + } + } + + class TestContent : INavigableContent + { + public TestContent(TestContentType type, int id, int parentId) + { + _type = type; + Id = id; + ParentId = parentId; + } + + private readonly TestContentType _type; + public int Id { get; private set; } + public int ParentId { get; private set; } + public INavigableContentType Type { get { return _type; } } + public IList ChildIds { get; private set; } + + public object Value(int id) + { + var fieldType = _type.FieldTypes[id] as TestPropertyType; + if (fieldType == null) throw new Exception("Oops"); + + var value = FieldValues[id]; + var isAttr = id <= _type.Source.LastAttributeIndex; + + // null => return null + if (value == null) return null; + + // attribute => return string value + if (isAttr) return value.ToString(); + + // has a converter => use the converter + if (fieldType.XmlStringConverter != null) + return fieldType.XmlStringConverter(value); + + // not a string => return value as a string + var s = value as string; + if (s == null) return value.ToString(); + + // xml content... try xml + if (fieldType.IsXmlContent) + { + XPathDocument doc; + if (XmlHelper.TryCreateXPathDocumentFromPropertyValue(s, out doc)) + return doc.CreateNavigator(); + } + + // return the string + // even if it's xml that can't be parsed... + return s; + } + + // locals + public object[] FieldValues { get; private set; } + + public TestContent WithChildren(params int[] childIds) + { + ChildIds = childIds; + return this; + } + + public TestContent WithValues(params object[] values) + { + FieldValues = values == null ? new object[] {null} : values; + return this; + } + } + + class TestRootContent : TestContent + { + public TestRootContent(TestContentType type) + : base(type, -1, -1) + { } + } + + abstract class TestSourceBase : INavigableSource + { + protected readonly Dictionary Content = new Dictionary(); + + public INavigableContent Get(int id) + { + return Content.ContainsKey(id) ? Content[id] : null; + } + + public int LastAttributeIndex { get; protected set; } + + public INavigableContent Root { get; protected set; } + } + + #endregion + + #region Navigable sources + + class TestSource0 : TestSourceBase + { + public TestSource0() + { + LastAttributeIndex = -1; + var type = new TestRootContentType(this); + Root = new TestRootContent(type); + } + } + + class TestSource1 : TestSourceBase + { + public TestSource1() + { + // last attribute index is 1 - meaning properties 0 and 1 are attributes, 2+ are elements + // then, fieldValues must have adequate number of items + LastAttributeIndex = 1; + + var prop1 = new TestPropertyType("prop1"); + var prop2 = new TestPropertyType("prop2"); + var prop3 = new TestPropertyType("prop3"); + var type = new TestRootContentType(this, prop1, prop2); + var type1 = type.CreateType("type1", prop3); + + Content[1] = new TestContent(type1, 1, -1).WithValues("1:p1", "1:p2", "1:p3"); + + Root = new TestRootContent(type).WithValues("", "").WithChildren(1); + } + } + + class TestSource2 : TestSourceBase + { + public TestSource2() + { + LastAttributeIndex = -1; + + var prop1 = new TestPropertyType("prop1", true); + var type = new TestRootContentType(this); + var type1 = type.CreateType("type1", prop1); + + const string xml = "poo"; + Content[1] = new TestContent(type1, 1, 1).WithValues(xml); + + Root = new TestRootContent(type).WithChildren(1); + } + } + + class TestSource3 : TestSourceBase + { + public TestSource3() + { + LastAttributeIndex = 1; + + var prop1 = new TestPropertyType("prop1"); + var prop2 = new TestPropertyType("prop2"); + var prop3 = new TestPropertyType("prop3"); + var type = new TestRootContentType(this, prop1, prop2); + var type1 = type.CreateType("type1", prop3); + + Content[1] = new TestContent(type1, 1, 1).WithValues("1:p1", "1:p2", "1:p3").WithChildren(2); + Content[2] = new TestContent(type1, 2, 1).WithValues("2:p1", "2:p2", "2:p3"); + + Root = new TestRootContent(type).WithChildren(1); + } + } + + class TestSource4 : TestSourceBase + { + public TestSource4() + { + LastAttributeIndex = -1; + + var prop1 = new TestPropertyType("prop1", true); + var prop2 = new TestPropertyType("prop2"); + var type = new TestRootContentType(this); + var type1 = type.CreateType("type1", prop1, prop2); + + Content[1] = new TestContent(type1, 1, -1).WithValues("", "dang"); + Content[2] = new TestContent(type1, 2, -1).WithValues(null, ""); + Content[3] = new TestContent(type1, 3, -1).WithValues(null, null); + + Root = new TestRootContent(type).WithChildren(1, 2, 3); + } + } + + class TestSource5 : TestSourceBase + { + public TestSource5() + { + LastAttributeIndex = -1; + + var prop1 = new TestPropertyType("prop1"); + var prop2 = new TestPropertyType("prop2"); + var type = new TestRootContentType(this); + var type1 = type.CreateType("type1", prop1, prop2); + + Content[1] = new TestContent(type1, 1, -1).WithValues("p11", "p12").WithChildren(3, 5); + Content[2] = new TestContent(type1, 2, -1).WithValues("p21", "p22").WithChildren(4, 6); + Content[3] = new TestContent(type1, 3, 1).WithValues("p31", "p32"); + Content[4] = new TestContent(type1, 4, 2).WithValues("p41", "p42"); + Content[5] = new TestContent(type1, 5, 1).WithValues("p51", "p52"); + Content[6] = new TestContent(type1, 6, 2).WithValues("p61", "p62"); + + Root = new TestRootContent(type).WithChildren(1, 2); + } + } + + class TestSource6 : TestSourceBase + { + // + // + // + // + // + // + // + // + // blah + // + // bam + // + // + // + // + // + + public TestSource6() + { + LastAttributeIndex = -1; + + var type = new TestRootContentType(this); + var type1 = type.CreateType("wrap", + new TestPropertyType("item1"), + new TestPropertyType("item2"), + new TestPropertyType("item2a"), + new TestPropertyType("item2b"), + new TestPropertyType("item2c"), + new TestPropertyType("item3"), + new TestPropertyType("item3a"), + new TestPropertyType("item4", true), + new TestPropertyType("item5", true) + ); + + Content[1] = new TestContent(type1, 1, -1) + .WithValues( + null, + null, + null, + null, + "\n ", + "blah", + "\n blah\n ", + "bam", + "\n " + ); + + Root = new TestRootContent(type).WithChildren(1); + } + } + + class TestSource7 : TestSourceBase + { + public TestSource7() + { + LastAttributeIndex = 0; + + var prop1 = new TestPropertyType("isDoc"); + var prop2 = new TestPropertyType("title"); + var type = new TestRootContentType(this, prop1); + var type1 = type.CreateType("node", prop1, prop2); + + Content[1] = new TestContent(type1, 1, -1).WithValues(1, "title-1").WithChildren(3, 5); + Content[2] = new TestContent(type1, 2, -1).WithValues(1, "title-2").WithChildren(4, 6); + Content[3] = new TestContent(type1, 3, 1).WithValues(1, "title-3").WithChildren(7, 8); + Content[4] = new TestContent(type1, 4, 2).WithValues(1, "title-4"); + Content[5] = new TestContent(type1, 5, 1).WithValues(1, "title-5"); + Content[6] = new TestContent(type1, 6, 2).WithValues(1, "title-6"); + + Content[7] = new TestContent(type1, 7, 3).WithValues(1, "title-7"); + Content[8] = new TestContent(type1, 8, 3).WithValues(1, "title-8"); + + Root = new TestRootContent(type).WithValues(null).WithChildren(1, 2); + } + } + + class TestSource8 : TestSourceBase + { + public TestSource8() + { + LastAttributeIndex = 0; + + var attr = new TestPropertyType("attr"); + var prop = new TestPropertyType("prop"); + var type = new TestRootContentType(this, attr); + var type1 = type.CreateType("item", attr, prop); + Content[1] = new TestContent(type1, 1, -1).WithValues(null, null); + Content[2] = new TestContent(type1, 2, -1).WithValues("", ""); + Content[3] = new TestContent(type1, 3, -1).WithValues(" ", " "); + Content[4] = new TestContent(type1, 4, -1).WithValues("", "\n"); + Content[5] = new TestContent(type1, 5, -1).WithValues(" ooo ", " ooo "); + Root = new TestRootContent(type).WithValues(null).WithChildren(1, 2, 3, 4, 5); + } + } + + #endregion + + static class StringCrLfExtensions + { + public static string Lf(this string s) + { + if (string.IsNullOrEmpty(s)) return s; + return s.Replace("\r", ""); // remove Cr + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Dynamics/QueryableExtensionTests.cs b/src/Umbraco.Tests/Dynamics/QueryableExtensionTests.cs index e93b7dc50a..d5343ff521 100644 --- a/src/Umbraco.Tests/Dynamics/QueryableExtensionTests.cs +++ b/src/Umbraco.Tests/Dynamics/QueryableExtensionTests.cs @@ -1,162 +1,162 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NUnit.Framework; -using Umbraco.Core.Dynamics; -using Umbraco.Web.Dynamics; -using Umbraco.Core.Models; - -namespace Umbraco.Tests.Dynamics -{ - //NOTE: there's libraries in both Umbraco.Core.Dynamics and Umbraco.Web.Dynamics - the reason for this is that the Web.Dynamics - // started with the razor macro implementation and is modified with hard coded references to dynamic node and dynamic null, though it seems - // to still work for other regular classes I don't want to move it to the core without removing these references but that would require a lot of work. - - [TestFixture] - public class QueryableExtensionTests - { - - [Test] - public void Order_By_Test_Int() - { - var items = new List - { - new TestModel {Age = 10, Name = "test1", Female = false}, - new TestModel {Age = 31, Name = "someguy", Female = true}, - new TestModel {Age = 11, Name = "test2", Female = true}, - new TestModel {Age = 20, Name = "anothertest", Female = false}, - new TestModel {Age = 55, Name = "blah", Female = false}, - new TestModel {Age = 12, Name = "test3", Female = false} - }; - - var result = items.AsQueryable().OrderBy("Age").ToArray(); - - Assert.AreEqual(10, result.ElementAt(0).Age); - Assert.AreEqual(11, result.ElementAt(1).Age); - Assert.AreEqual(12, result.ElementAt(2).Age); - Assert.AreEqual(20, result.ElementAt(3).Age); - Assert.AreEqual(31, result.ElementAt(4).Age); - Assert.AreEqual(55, result.ElementAt(5).Age); - - } - - [Test] - public void Order_By_Test_String() - { - var items = new List - { - new TestModel {Age = 10, Name = "test1", Female = false}, - new TestModel {Age = 31, Name = "someguy", Female = true}, - new TestModel {Age = 11, Name = "test2", Female = true}, - new TestModel {Age = 20, Name = "anothertest", Female = false}, - new TestModel {Age = 55, Name = "blah", Female = false}, - new TestModel {Age = 12, Name = "test3", Female = false} - }; - - var result = items.AsQueryable().OrderBy("Name").ToArray(); - - Assert.AreEqual("anothertest", result.ElementAt(0).Name); - Assert.AreEqual("blah", result.ElementAt(1).Name); - Assert.AreEqual("someguy", result.ElementAt(2).Name); - Assert.AreEqual("test1", result.ElementAt(3).Name); - Assert.AreEqual("test2", result.ElementAt(4).Name); - Assert.AreEqual("test3", result.ElementAt(5).Name); - - } - - [Test] - public void Where_Test_String() - { - var items = new List - { - new TestModel {Age = 10, Name = "test1", Female = false}, - new TestModel {Age = 31, Name = "someguy", Female = true}, - new TestModel {Age = 11, Name = "test2", Female = true}, - new TestModel {Age = 20, Name = "anothertest", Female = false}, - new TestModel {Age = 55, Name = "blah", Female = false}, - new TestModel {Age = 12, Name = "test3", Female = false} - }; - - var result = items.AsQueryable().Where("Name = \"test1\"").ToArray(); - - Assert.AreEqual(1, result.Count()); - Assert.AreEqual("test1", result.ElementAt(0).Name); - - - } - - [Test] - public void Where_Test_String_With_Params() - { - var items = new List - { - new TestModel {Age = 10, Name = "test1", Female = false}, - new TestModel {Age = 31, Name = "someguy", Female = true}, - new TestModel {Age = 11, Name = "test2", Female = true}, - new TestModel {Age = 20, Name = "anothertest", Female = false}, - new TestModel {Age = 55, Name = "blah", Female = false}, - new TestModel {Age = 12, Name = "test3", Female = false} - }; - - //NOTE: Currently the object query structure is not supported - //var result = items.AsQueryable().Where("Name = @name", new {name = "test1"}).ToArray(); - var result = items.AsQueryable().Where("Name = @Name", new Dictionary { { "Name", "test1" } }).ToArray(); - - Assert.AreEqual(1, result.Count()); - Assert.AreEqual("test1", result.ElementAt(0).Name); - - - } - - [Test] - public void Where_Test_Int_With_Params() - { - var items = new List - { - new TestModel {Age = 10, Name = "test1", Female = false}, - new TestModel {Age = 31, Name = "someguy", Female = true}, - new TestModel {Age = 11, Name = "test2", Female = true}, - new TestModel {Age = 20, Name = "anothertest", Female = false}, - new TestModel {Age = 55, Name = "blah", Female = false}, - new TestModel {Age = 12, Name = "test3", Female = false} - }; - - var result = items.AsQueryable().Where("Age = @Age", new Dictionary { { "Age", 10 } }).ToArray(); - - Assert.AreEqual(1, result.Count()); - Assert.AreEqual("test1", result.ElementAt(0).Name); - - - } - - [Test] - public void Where_Test_Bool_With_Params() - { - var items = new List - { - new TestModel {Age = 10, Name = "test1", Female = false}, - new TestModel {Age = 31, Name = "someguy", Female = true}, - new TestModel {Age = 11, Name = "test2", Female = true}, - new TestModel {Age = 20, Name = "anothertest", Female = false}, - new TestModel {Age = 55, Name = "blah", Female = false}, - new TestModel {Age = 12, Name = "test3", Female = false} - }; - - var result = items.AsQueryable().Where("Female = @Female", new Dictionary { { "Female", true } }).ToArray(); - - Assert.AreEqual(2, result.Count()); - - - } - - private class TestModel - { - public string Name { get; set; } - public int Age { get; set; } - public bool Female { get; set; } - } - - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NUnit.Framework; +using Umbraco.Core.Dynamics; +using Umbraco.Web.Dynamics; +using Umbraco.Core.Models; + +namespace Umbraco.Tests.Dynamics +{ + //NOTE: there's libraries in both Umbraco.Core.Dynamics and Umbraco.Web.Dynamics - the reason for this is that the Web.Dynamics + // started with the razor macro implementation and is modified with hard coded references to dynamic node and dynamic null, though it seems + // to still work for other regular classes I don't want to move it to the core without removing these references but that would require a lot of work. + + [TestFixture] + public class QueryableExtensionTests + { + + [Test] + public void Order_By_Test_Int() + { + var items = new List + { + new TestModel {Age = 10, Name = "test1", Female = false}, + new TestModel {Age = 31, Name = "someguy", Female = true}, + new TestModel {Age = 11, Name = "test2", Female = true}, + new TestModel {Age = 20, Name = "anothertest", Female = false}, + new TestModel {Age = 55, Name = "blah", Female = false}, + new TestModel {Age = 12, Name = "test3", Female = false} + }; + + var result = items.AsQueryable().OrderBy("Age").ToArray(); + + Assert.AreEqual(10, result.ElementAt(0).Age); + Assert.AreEqual(11, result.ElementAt(1).Age); + Assert.AreEqual(12, result.ElementAt(2).Age); + Assert.AreEqual(20, result.ElementAt(3).Age); + Assert.AreEqual(31, result.ElementAt(4).Age); + Assert.AreEqual(55, result.ElementAt(5).Age); + + } + + [Test] + public void Order_By_Test_String() + { + var items = new List + { + new TestModel {Age = 10, Name = "test1", Female = false}, + new TestModel {Age = 31, Name = "someguy", Female = true}, + new TestModel {Age = 11, Name = "test2", Female = true}, + new TestModel {Age = 20, Name = "anothertest", Female = false}, + new TestModel {Age = 55, Name = "blah", Female = false}, + new TestModel {Age = 12, Name = "test3", Female = false} + }; + + var result = items.AsQueryable().OrderBy("Name").ToArray(); + + Assert.AreEqual("anothertest", result.ElementAt(0).Name); + Assert.AreEqual("blah", result.ElementAt(1).Name); + Assert.AreEqual("someguy", result.ElementAt(2).Name); + Assert.AreEqual("test1", result.ElementAt(3).Name); + Assert.AreEqual("test2", result.ElementAt(4).Name); + Assert.AreEqual("test3", result.ElementAt(5).Name); + + } + + [Test] + public void Where_Test_String() + { + var items = new List + { + new TestModel {Age = 10, Name = "test1", Female = false}, + new TestModel {Age = 31, Name = "someguy", Female = true}, + new TestModel {Age = 11, Name = "test2", Female = true}, + new TestModel {Age = 20, Name = "anothertest", Female = false}, + new TestModel {Age = 55, Name = "blah", Female = false}, + new TestModel {Age = 12, Name = "test3", Female = false} + }; + + var result = items.AsQueryable().Where("Name = \"test1\"").ToArray(); + + Assert.AreEqual(1, result.Count()); + Assert.AreEqual("test1", result.ElementAt(0).Name); + + + } + + [Test] + public void Where_Test_String_With_Params() + { + var items = new List + { + new TestModel {Age = 10, Name = "test1", Female = false}, + new TestModel {Age = 31, Name = "someguy", Female = true}, + new TestModel {Age = 11, Name = "test2", Female = true}, + new TestModel {Age = 20, Name = "anothertest", Female = false}, + new TestModel {Age = 55, Name = "blah", Female = false}, + new TestModel {Age = 12, Name = "test3", Female = false} + }; + + //NOTE: Currently the object query structure is not supported + //var result = items.AsQueryable().Where("Name = @name", new {name = "test1"}).ToArray(); + var result = items.AsQueryable().Where("Name = @Name", new Dictionary { { "Name", "test1" } }).ToArray(); + + Assert.AreEqual(1, result.Count()); + Assert.AreEqual("test1", result.ElementAt(0).Name); + + + } + + [Test] + public void Where_Test_Int_With_Params() + { + var items = new List + { + new TestModel {Age = 10, Name = "test1", Female = false}, + new TestModel {Age = 31, Name = "someguy", Female = true}, + new TestModel {Age = 11, Name = "test2", Female = true}, + new TestModel {Age = 20, Name = "anothertest", Female = false}, + new TestModel {Age = 55, Name = "blah", Female = false}, + new TestModel {Age = 12, Name = "test3", Female = false} + }; + + var result = items.AsQueryable().Where("Age = @Age", new Dictionary { { "Age", 10 } }).ToArray(); + + Assert.AreEqual(1, result.Count()); + Assert.AreEqual("test1", result.ElementAt(0).Name); + + + } + + [Test] + public void Where_Test_Bool_With_Params() + { + var items = new List + { + new TestModel {Age = 10, Name = "test1", Female = false}, + new TestModel {Age = 31, Name = "someguy", Female = true}, + new TestModel {Age = 11, Name = "test2", Female = true}, + new TestModel {Age = 20, Name = "anothertest", Female = false}, + new TestModel {Age = 55, Name = "blah", Female = false}, + new TestModel {Age = 12, Name = "test3", Female = false} + }; + + var result = items.AsQueryable().Where("Female = @Female", new Dictionary { { "Female", true } }).ToArray(); + + Assert.AreEqual(2, result.Count()); + + + } + + private class TestModel + { + public string Name { get; set; } + public int Age { get; set; } + public bool Female { get; set; } + } + + } +} diff --git a/src/Umbraco.Tests/DynamicsAndReflection/ExtensionMethodFinderTests.cs b/src/Umbraco.Tests/DynamicsAndReflection/ExtensionMethodFinderTests.cs index 003eb31d78..56fc36a123 100644 --- a/src/Umbraco.Tests/DynamicsAndReflection/ExtensionMethodFinderTests.cs +++ b/src/Umbraco.Tests/DynamicsAndReflection/ExtensionMethodFinderTests.cs @@ -1,198 +1,198 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using NUnit.Framework; -using Umbraco.Core; -using Umbraco.Core.Dynamics; - -namespace Umbraco.Tests.DynamicsAndReflection -{ - [TestFixture] - public class ExtensionMethodFinderTests - { - // To expand on Jon's answer, the reason this doesn't work is because in regular, - // non-dynamic code extension methods work by doing a full search of all the - // classes known to the compiler for a static class that has an extension method - // that match. The search goes in order based on the namespace nesting and available - // "using" directives in each namespace. - // - // That means that in order to get a dynamic extension method invocation resolved - // correctly, somehow the DLR has to know at runtime what all the namespace nestings - // and "using" directives were in your source code. We do not have a mechanism handy - // for encoding all that information into the call site. We considered inventing - // such a mechanism, but decided that it was too high cost and produced too much - // schedule risk to be worth it. - // - // Eric Lippert, http://stackoverflow.com/questions/5311465/extension-method-and-dynamic-object-in-c-sharp - - [Test] - [Ignore("fails")] - public void TypesTests() - { - Assert.IsTrue(typeof(int[]).Inherits()); - Assert.IsFalse(typeof(int[]).Inherits()); - - var m1 = typeof (ExtensionMethodFinderTests).GetMethod("TestMethod1"); - - var a1A = new object[] {1}; - var m1A = GetMethodForArguments(m1, a1A); - Assert.IsNotNull(m1A); - m1A.Invoke(this, a1A); - - var a1B = new object[] {"foo"}; - var m1B = GetMethodForArguments(m1, a1B); - Assert.IsNull(m1B); - - var m2 = typeof(ExtensionMethodFinderTests).GetMethod("TestMethod2"); - - var m2A = GetMethodForArguments(m2, a1A); - Assert.IsNotNull(m2A); - m2A.Invoke(this, a1A); - - var m2B = GetMethodForArguments(m2, a1B); - Assert.IsNotNull(m2B); - m2B.Invoke(this, a1B); - - var m3 = typeof(ExtensionMethodFinderTests).GetMethod("TestMethod3"); - - var a3A = new object[] {1, 2}; - var m3A = GetMethodForArguments(m3, a3A); - Assert.IsNotNull(m3A); - m3A.Invoke(this, a3A); - - var a3B = new object[] {1, "foo"}; - var m3B = GetMethodForArguments(m3, a3B); - Assert.IsNull(m3B); - - var m4 = typeof(ExtensionMethodFinderTests).GetMethod("TestMethod4"); - - var m4A = GetMethodForArguments(m4, a3A); - Assert.IsNotNull(m4A); - m4A.Invoke(this, a3A); - - var m4B = GetMethodForArguments(m4, a3B); - Assert.IsNotNull(m4B); - m4B.Invoke(this, a3B); - - var m5 = typeof(ExtensionMethodFinderTests).GetMethod("TestMethod5"); - - // note - currently that fails because we can't match List with List - var a5 = new object[] {new List()}; - var m5A = GetMethodForArguments(m5, a5); - Assert.IsNotNull(m5A); - - // note - should we also handle "ref" and "out" parameters? - // note - should we pay attention to array types? - } - - public void TestMethod1(int value) {} - public void TestMethod2(T value) {} - public void TestMethod3(T value1, T value2) { } - public void TestMethod4(T1 value1, T2 value2) { } - public void TestMethod5(List value) { } - - // gets the method that can apply to the arguments - // either the method itself, or a generic one - // or null if it couldn't match - // - // this is a nightmare - if we want to do it right, then we have - // to re-do the whole compiler type inference stuff by ourselves?! - // - static MethodInfo GetMethodForArguments(MethodInfo method, IList arguments) - { - var parameters = method.GetParameters(); - var genericArguments = method.GetGenericArguments(); - - if (parameters.Length != arguments.Count) return null; - - var genericArgumentTypes = new Type[genericArguments.Length]; - var i = 0; - for (; i < parameters.Length; i++) - { - var parameterType = parameters[i].ParameterType; - var argumentType = arguments[i].GetType(); - - Console.WriteLine("{0} / {1}", parameterType, argumentType); - - if (parameterType == argumentType) continue; // match - if (parameterType.IsGenericParameter) // eg T - { - var pos = parameterType.GenericParameterPosition; - if (genericArgumentTypes[pos] != null) - { - // note - is this OK? what about variance and such? - // it is NOT ok, if the first pass is SomethingElse then next is Something - // it will fail... the specs prob. indicate how it works, trying to find a common - // type... - if (genericArgumentTypes[pos].IsAssignableFrom(argumentType) == false) - break; - } - else - { - genericArgumentTypes[pos] = argumentType; - } - } - else if (parameterType.IsGenericType) // eg List - { - if (argumentType.IsGenericType == false) break; - - var pg = parameterType.GetGenericArguments(); - var ag = argumentType.GetGenericArguments(); - - // then what ?! - // should _variance_ be of some importance? - Console.WriteLine("generic {0}", argumentType.IsGenericType); - } - else - { - if (parameterType.IsAssignableFrom(argumentType) == false) - break; - } - } - if (i != parameters.Length) return null; - return genericArguments.Length == 0 - ? method - : method.MakeGenericMethod(genericArgumentTypes); - } - - public class Class1 - {} - - [Test] - [Ignore("fails")] - public void FinderTests() - { - MethodInfo method; - var class1 = new Class1(); - - method = ExtensionMethodFinder.FindExtensionMethod(typeof (Class1), new object[] {1}, "TestMethod1", false); - Assert.IsNotNull(method); - method.Invoke(null, new object[] { class1, 1 }); - - method = ExtensionMethodFinder.FindExtensionMethod(typeof(Class1), new object[] { "x" }, "TestMethod1", false); - Assert.IsNull(method); // note - fails, return TestMethod1! - - method = ExtensionMethodFinder.FindExtensionMethod(typeof(Class1), new object[] { 1 }, "TestMethod2", false); - Assert.IsNotNull(method); - method.Invoke(null, new object[] { class1, "1" }); - - method = ExtensionMethodFinder.FindExtensionMethod(typeof(Class1), new object[] { "x" }, "TestMethod2", false); - Assert.IsNotNull(method); - method.Invoke(null, new object[] { class1, "x" }); - } - } - - static class ExtensionMethodFinderTestsExtensions - { - public static void TestMethod1(this ExtensionMethodFinderTests.Class1 source, int value) - { } - - public static void TestMethod2(this ExtensionMethodFinderTests.Class1 source, int value) - { } - - public static void TestMethod2(this ExtensionMethodFinderTests.Class1 source, string value) - { } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Dynamics; + +namespace Umbraco.Tests.DynamicsAndReflection +{ + [TestFixture] + public class ExtensionMethodFinderTests + { + // To expand on Jon's answer, the reason this doesn't work is because in regular, + // non-dynamic code extension methods work by doing a full search of all the + // classes known to the compiler for a static class that has an extension method + // that match. The search goes in order based on the namespace nesting and available + // "using" directives in each namespace. + // + // That means that in order to get a dynamic extension method invocation resolved + // correctly, somehow the DLR has to know at runtime what all the namespace nestings + // and "using" directives were in your source code. We do not have a mechanism handy + // for encoding all that information into the call site. We considered inventing + // such a mechanism, but decided that it was too high cost and produced too much + // schedule risk to be worth it. + // + // Eric Lippert, http://stackoverflow.com/questions/5311465/extension-method-and-dynamic-object-in-c-sharp + + [Test] + [Ignore("fails")] + public void TypesTests() + { + Assert.IsTrue(typeof(int[]).Inherits()); + Assert.IsFalse(typeof(int[]).Inherits()); + + var m1 = typeof (ExtensionMethodFinderTests).GetMethod("TestMethod1"); + + var a1A = new object[] {1}; + var m1A = GetMethodForArguments(m1, a1A); + Assert.IsNotNull(m1A); + m1A.Invoke(this, a1A); + + var a1B = new object[] {"foo"}; + var m1B = GetMethodForArguments(m1, a1B); + Assert.IsNull(m1B); + + var m2 = typeof(ExtensionMethodFinderTests).GetMethod("TestMethod2"); + + var m2A = GetMethodForArguments(m2, a1A); + Assert.IsNotNull(m2A); + m2A.Invoke(this, a1A); + + var m2B = GetMethodForArguments(m2, a1B); + Assert.IsNotNull(m2B); + m2B.Invoke(this, a1B); + + var m3 = typeof(ExtensionMethodFinderTests).GetMethod("TestMethod3"); + + var a3A = new object[] {1, 2}; + var m3A = GetMethodForArguments(m3, a3A); + Assert.IsNotNull(m3A); + m3A.Invoke(this, a3A); + + var a3B = new object[] {1, "foo"}; + var m3B = GetMethodForArguments(m3, a3B); + Assert.IsNull(m3B); + + var m4 = typeof(ExtensionMethodFinderTests).GetMethod("TestMethod4"); + + var m4A = GetMethodForArguments(m4, a3A); + Assert.IsNotNull(m4A); + m4A.Invoke(this, a3A); + + var m4B = GetMethodForArguments(m4, a3B); + Assert.IsNotNull(m4B); + m4B.Invoke(this, a3B); + + var m5 = typeof(ExtensionMethodFinderTests).GetMethod("TestMethod5"); + + // note - currently that fails because we can't match List with List + var a5 = new object[] {new List()}; + var m5A = GetMethodForArguments(m5, a5); + Assert.IsNotNull(m5A); + + // note - should we also handle "ref" and "out" parameters? + // note - should we pay attention to array types? + } + + public void TestMethod1(int value) {} + public void TestMethod2(T value) {} + public void TestMethod3(T value1, T value2) { } + public void TestMethod4(T1 value1, T2 value2) { } + public void TestMethod5(List value) { } + + // gets the method that can apply to the arguments + // either the method itself, or a generic one + // or null if it couldn't match + // + // this is a nightmare - if we want to do it right, then we have + // to re-do the whole compiler type inference stuff by ourselves?! + // + static MethodInfo GetMethodForArguments(MethodInfo method, IList arguments) + { + var parameters = method.GetParameters(); + var genericArguments = method.GetGenericArguments(); + + if (parameters.Length != arguments.Count) return null; + + var genericArgumentTypes = new Type[genericArguments.Length]; + var i = 0; + for (; i < parameters.Length; i++) + { + var parameterType = parameters[i].ParameterType; + var argumentType = arguments[i].GetType(); + + Console.WriteLine("{0} / {1}", parameterType, argumentType); + + if (parameterType == argumentType) continue; // match + if (parameterType.IsGenericParameter) // eg T + { + var pos = parameterType.GenericParameterPosition; + if (genericArgumentTypes[pos] != null) + { + // note - is this OK? what about variance and such? + // it is NOT ok, if the first pass is SomethingElse then next is Something + // it will fail... the specs prob. indicate how it works, trying to find a common + // type... + if (genericArgumentTypes[pos].IsAssignableFrom(argumentType) == false) + break; + } + else + { + genericArgumentTypes[pos] = argumentType; + } + } + else if (parameterType.IsGenericType) // eg List + { + if (argumentType.IsGenericType == false) break; + + var pg = parameterType.GetGenericArguments(); + var ag = argumentType.GetGenericArguments(); + + // then what ?! + // should _variance_ be of some importance? + Console.WriteLine("generic {0}", argumentType.IsGenericType); + } + else + { + if (parameterType.IsAssignableFrom(argumentType) == false) + break; + } + } + if (i != parameters.Length) return null; + return genericArguments.Length == 0 + ? method + : method.MakeGenericMethod(genericArgumentTypes); + } + + public class Class1 + {} + + [Test] + [Ignore("fails")] + public void FinderTests() + { + MethodInfo method; + var class1 = new Class1(); + + method = ExtensionMethodFinder.FindExtensionMethod(typeof (Class1), new object[] {1}, "TestMethod1", false); + Assert.IsNotNull(method); + method.Invoke(null, new object[] { class1, 1 }); + + method = ExtensionMethodFinder.FindExtensionMethod(typeof(Class1), new object[] { "x" }, "TestMethod1", false); + Assert.IsNull(method); // note - fails, return TestMethod1! + + method = ExtensionMethodFinder.FindExtensionMethod(typeof(Class1), new object[] { 1 }, "TestMethod2", false); + Assert.IsNotNull(method); + method.Invoke(null, new object[] { class1, "1" }); + + method = ExtensionMethodFinder.FindExtensionMethod(typeof(Class1), new object[] { "x" }, "TestMethod2", false); + Assert.IsNotNull(method); + method.Invoke(null, new object[] { class1, "x" }); + } + } + + static class ExtensionMethodFinderTestsExtensions + { + public static void TestMethod1(this ExtensionMethodFinderTests.Class1 source, int value) + { } + + public static void TestMethod2(this ExtensionMethodFinderTests.Class1 source, int value) + { } + + public static void TestMethod2(this ExtensionMethodFinderTests.Class1 source, string value) + { } + } +} diff --git a/src/Umbraco.Tests/DynamicsAndReflection/ReflectionTests.cs b/src/Umbraco.Tests/DynamicsAndReflection/ReflectionTests.cs index 857517724f..974ae5ddfd 100644 --- a/src/Umbraco.Tests/DynamicsAndReflection/ReflectionTests.cs +++ b/src/Umbraco.Tests/DynamicsAndReflection/ReflectionTests.cs @@ -1,73 +1,73 @@ -using System.Linq; -using NUnit.Framework; -using Umbraco.Core; - -namespace Umbraco.Tests.DynamicsAndReflection -{ - [TestFixture] - public class ReflectionTests - { - [Test] - public void GetBaseTypesIsOk() - { - // tests that the GetBaseTypes extension method works. - - var type = typeof(Class2); - var types = type.GetBaseTypes(true).ToArray(); - Assert.AreEqual(3, types.Length); - Assert.Contains(typeof(Class2), types); - Assert.Contains(typeof(Class1), types); - Assert.Contains(typeof(object), types); - - types = type.GetBaseTypes(false).ToArray(); - Assert.AreEqual(2, types.Length); - Assert.Contains(typeof(Class1), types); - Assert.Contains(typeof(object), types); - } - - [Test] - public void GetInterfacesIsOk() - { - // tests that GetInterfaces gets _all_ interfaces - // so the AllInterfaces extension method is useless - - var type = typeof(Class2); - var interfaces = type.GetInterfaces(); - Assert.AreEqual(2, interfaces.Length); - Assert.Contains(typeof(IInterface1), interfaces); - Assert.Contains(typeof(IInterface2), interfaces); - } - - // TypeExtensions.AllInterfaces was broken an not used, has been commented out - // - //[Test] - //public void AllInterfacesIsBroken() - //{ - // // tests that the AllInterfaces extension method is broken - // - // var type = typeof(Class2); - // var interfaces = type.AllInterfaces().ToArray(); - // Assert.AreEqual(3, interfaces.Length); // should be 2! - // Assert.Contains(typeof(IInterface1), interfaces); - // Assert.Contains(typeof(IInterface2), interfaces); - // Assert.AreEqual(2, interfaces.Count(i => i == typeof(IInterface1))); // duplicate! - // Assert.AreEqual(1, interfaces.Count(i => i == typeof(IInterface2))); - //} - - interface IInterface1 - { } - - interface IInterface2 : IInterface1 - { - void Method(); - } - - class Class1 : IInterface2 - { - public void Method() { } - } - - class Class2 : Class1 - { } - } -} +using System.Linq; +using NUnit.Framework; +using Umbraco.Core; + +namespace Umbraco.Tests.DynamicsAndReflection +{ + [TestFixture] + public class ReflectionTests + { + [Test] + public void GetBaseTypesIsOk() + { + // tests that the GetBaseTypes extension method works. + + var type = typeof(Class2); + var types = type.GetBaseTypes(true).ToArray(); + Assert.AreEqual(3, types.Length); + Assert.Contains(typeof(Class2), types); + Assert.Contains(typeof(Class1), types); + Assert.Contains(typeof(object), types); + + types = type.GetBaseTypes(false).ToArray(); + Assert.AreEqual(2, types.Length); + Assert.Contains(typeof(Class1), types); + Assert.Contains(typeof(object), types); + } + + [Test] + public void GetInterfacesIsOk() + { + // tests that GetInterfaces gets _all_ interfaces + // so the AllInterfaces extension method is useless + + var type = typeof(Class2); + var interfaces = type.GetInterfaces(); + Assert.AreEqual(2, interfaces.Length); + Assert.Contains(typeof(IInterface1), interfaces); + Assert.Contains(typeof(IInterface2), interfaces); + } + + // TypeExtensions.AllInterfaces was broken an not used, has been commented out + // + //[Test] + //public void AllInterfacesIsBroken() + //{ + // // tests that the AllInterfaces extension method is broken + // + // var type = typeof(Class2); + // var interfaces = type.AllInterfaces().ToArray(); + // Assert.AreEqual(3, interfaces.Length); // should be 2! + // Assert.Contains(typeof(IInterface1), interfaces); + // Assert.Contains(typeof(IInterface2), interfaces); + // Assert.AreEqual(2, interfaces.Count(i => i == typeof(IInterface1))); // duplicate! + // Assert.AreEqual(1, interfaces.Count(i => i == typeof(IInterface2))); + //} + + interface IInterface1 + { } + + interface IInterface2 : IInterface1 + { + void Method(); + } + + class Class1 : IInterface2 + { + public void Method() { } + } + + class Class2 : Class1 + { } + } +} diff --git a/src/Umbraco.Tests/Macros/MacroParserTests.cs b/src/Umbraco.Tests/Macros/MacroParserTests.cs index 1e2f696373..56fc4f2062 100644 --- a/src/Umbraco.Tests/Macros/MacroParserTests.cs +++ b/src/Umbraco.Tests/Macros/MacroParserTests.cs @@ -1,162 +1,162 @@ -using System; -using System.Collections.Generic; -using NUnit.Framework; -using Umbraco.Core.Macros; - -namespace Umbraco.Tests.Macros -{ - [TestFixture] - public class MacroParserTests - { - [Test] - public void Format_RTE_Data_For_Editor() - { - var content = @"

asdfasdf

-

asdfsadf

- -

asdfasdf

"; - var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary(){{"test1", "value1"},{"test2", "value2"}}); - - Assert.AreEqual(@"

asdfasdf

-

asdfsadf

-
- -Macro alias: Map
-

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); - } - - [Test] - public void Format_RTE_Data_For_Editor_Closing_Tag() - { - var content = @"

asdfasdf

-

asdfsadf

- -

asdfasdf

"; - var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary() { { "test1", "value1" }, { "test2", "value2" } }); - - Assert.AreEqual(@"

asdfasdf

-

asdfsadf

-
- -Macro alias: Map
-

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); - } - - [Test] - public void Format_RTE_Data_For_Editor_With_Params() - { - var content = @"

asdfasdf

-

asdfsadf

- -

asdfasdf

"; - var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary() { { "test1", "value1" }, { "test2", "value2" } }); - - Assert.AreEqual(@"

asdfasdf

-

asdfsadf

-
- -Macro alias: Map
-

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); - } - - [Test] - public void Format_RTE_Data_For_Editor_With_Params_Closing_Tag() - { - var content = @"

asdfasdf

-

asdfsadf

- -

asdfasdf

"; - var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary() { { "test1", "value1" }, { "test2", "value2" } }); - - Assert.AreEqual(@"

asdfasdf

-

asdfsadf

-
- -Macro alias: Map
-

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); - } - - [Test] - public void Format_RTE_Data_For_Editor_With_Params_Closing_Tag_And_Content() - { - var content = @"

asdfasdf

-

asdfsadf

- -

asdfasdf

"; - var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary() { { "test1", "value1" }, { "test2", "value2" } }); - - Assert.AreEqual(@"

asdfasdf

-

asdfsadf

-
- -Macro alias: Map
-

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); - } - - [Test] - public void Format_RTE_Data_For_Persistence() - { - var content = @" - -

asdfasdf

-
- -asdfasdf -asdfas -asdfasdfasdf -

asdfasdf

-
-asdfdasf -
-asdfsdf -
- -"; - var result = MacroTagParser.FormatRichTextContentForPersistence(content); - - Assert.AreEqual(@" - -

asdfasdf

- -asdfdasf -
-asdfsdf -
- -".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); - } - - [Test] - public void Format_RTE_Data_For_Persistence_No_Class() - { - var content = @" - -

asdfasdf

-
- -asdfasdf -asdfas -asdfasdfasdf -

asdfasdf

-
-asdfdasf -
-asdfsdf -
- -"; - var result = MacroTagParser.FormatRichTextContentForPersistence(content); - - Assert.AreEqual(@" - -

asdfasdf

- -asdfdasf -
-asdfsdf -
- -".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); - } - } +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Umbraco.Core.Macros; + +namespace Umbraco.Tests.Macros +{ + [TestFixture] + public class MacroParserTests + { + [Test] + public void Format_RTE_Data_For_Editor() + { + var content = @"

asdfasdf

+

asdfsadf

+ +

asdfasdf

"; + var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary(){{"test1", "value1"},{"test2", "value2"}}); + + Assert.AreEqual(@"

asdfasdf

+

asdfsadf

+
+ +Macro alias: Map
+

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); + } + + [Test] + public void Format_RTE_Data_For_Editor_Closing_Tag() + { + var content = @"

asdfasdf

+

asdfsadf

+ +

asdfasdf

"; + var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary() { { "test1", "value1" }, { "test2", "value2" } }); + + Assert.AreEqual(@"

asdfasdf

+

asdfsadf

+
+ +Macro alias: Map
+

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); + } + + [Test] + public void Format_RTE_Data_For_Editor_With_Params() + { + var content = @"

asdfasdf

+

asdfsadf

+ +

asdfasdf

"; + var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary() { { "test1", "value1" }, { "test2", "value2" } }); + + Assert.AreEqual(@"

asdfasdf

+

asdfsadf

+
+ +Macro alias: Map
+

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); + } + + [Test] + public void Format_RTE_Data_For_Editor_With_Params_Closing_Tag() + { + var content = @"

asdfasdf

+

asdfsadf

+ +

asdfasdf

"; + var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary() { { "test1", "value1" }, { "test2", "value2" } }); + + Assert.AreEqual(@"

asdfasdf

+

asdfsadf

+
+ +Macro alias: Map
+

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); + } + + [Test] + public void Format_RTE_Data_For_Editor_With_Params_Closing_Tag_And_Content() + { + var content = @"

asdfasdf

+

asdfsadf

+ +

asdfasdf

"; + var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary() { { "test1", "value1" }, { "test2", "value2" } }); + + Assert.AreEqual(@"

asdfasdf

+

asdfsadf

+
+ +Macro alias: Map
+

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); + } + + [Test] + public void Format_RTE_Data_For_Persistence() + { + var content = @" + +

asdfasdf

+
+ +asdfasdf +asdfas +asdfasdfasdf +

asdfasdf

+
+asdfdasf +
+asdfsdf +
+ +"; + var result = MacroTagParser.FormatRichTextContentForPersistence(content); + + Assert.AreEqual(@" + +

asdfasdf

+ +asdfdasf +
+asdfsdf +
+ +".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); + } + + [Test] + public void Format_RTE_Data_For_Persistence_No_Class() + { + var content = @" + +

asdfasdf

+
+ +asdfasdf +asdfas +asdfasdfasdf +

asdfasdf

+
+asdfdasf +
+asdfsdf +
+ +"; + var result = MacroTagParser.FormatRichTextContentForPersistence(content); + + Assert.AreEqual(@" + +

asdfasdf

+ +asdfdasf +
+asdfsdf +
+ +".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs b/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs index 500b33501c..d1e0b2a190 100644 --- a/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs +++ b/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs @@ -1,69 +1,69 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Web.Security; -using NUnit.Framework; -using Umbraco.Core.Security; -using Umbraco.Web.Security.Providers; - -namespace Umbraco.Tests.Membership -{ - [TestFixture] - public class MembershipProviderBaseTests - { - [TestCase("hello", 0, "", 5, true)] - [TestCase("hello", 0, "", 4, true)] - [TestCase("hello", 0, "", 6, false)] - [TestCase("hello", 1, "", 5, false)] - [TestCase("hello!", 1, "", 0, true)] - [TestCase("hello!", 2, "", 0, false)] - [TestCase("hello!!", 2, "", 0, true)] - //8 characters or more in length, at least 1 lowercase letter,at least 1 character that is not a lower letter. - [TestCase("hello", 0, "(?=.{8,})[a-z]+[^a-z]+|[^a-z]+[a-z]+", 0, false)] - [TestCase("helloooo", 0, "(?=.{8,})[a-z]+[^a-z]+|[^a-z]+[a-z]+", 0, false)] - [TestCase("helloooO", 0, "(?=.{8,})[a-z]+[^a-z]+|[^a-z]+[a-z]+", 0, true)] - [TestCase("HELLOOOO", 0, "(?=.{8,})[a-z]+[^a-z]+|[^a-z]+[a-z]+", 0, false)] - [TestCase("HELLOOOo", 0, "(?=.{8,})[a-z]+[^a-z]+|[^a-z]+[a-z]+", 0, true)] - public void Valid_Password(string password, int minRequiredNonAlphanumericChars, string strengthRegex, int minLength, bool pass) - { - var result = MembershipProviderBase.IsPasswordValid(password, minRequiredNonAlphanumericChars, strengthRegex, minLength); - Assert.AreEqual(pass, result.Success); - } - - /// - /// The salt generated is always the same length - /// - [Test] - public void Check_Salt_Length() - { - var lastLength = 0; - for (var i = 0; i < 10000; i++) - { - var result = MembershipProviderBase.GenerateSalt(); - - if (i > 0) - { - Assert.AreEqual(lastLength, result.Length); - } - - lastLength = result.Length; - } - } - - [Test] - public void Get_StoredPassword() - { - var salt = MembershipProviderBase.GenerateSalt(); - var stored = salt + "ThisIsAHashedPassword"; - - string initSalt; - var result = MembershipProviderBase.StoredPassword(stored, MembershipPasswordFormat.Hashed, out initSalt); - - Assert.AreEqual(salt, initSalt); - } - - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.Security; +using NUnit.Framework; +using Umbraco.Core.Security; +using Umbraco.Web.Security.Providers; + +namespace Umbraco.Tests.Membership +{ + [TestFixture] + public class MembershipProviderBaseTests + { + [TestCase("hello", 0, "", 5, true)] + [TestCase("hello", 0, "", 4, true)] + [TestCase("hello", 0, "", 6, false)] + [TestCase("hello", 1, "", 5, false)] + [TestCase("hello!", 1, "", 0, true)] + [TestCase("hello!", 2, "", 0, false)] + [TestCase("hello!!", 2, "", 0, true)] + //8 characters or more in length, at least 1 lowercase letter,at least 1 character that is not a lower letter. + [TestCase("hello", 0, "(?=.{8,})[a-z]+[^a-z]+|[^a-z]+[a-z]+", 0, false)] + [TestCase("helloooo", 0, "(?=.{8,})[a-z]+[^a-z]+|[^a-z]+[a-z]+", 0, false)] + [TestCase("helloooO", 0, "(?=.{8,})[a-z]+[^a-z]+|[^a-z]+[a-z]+", 0, true)] + [TestCase("HELLOOOO", 0, "(?=.{8,})[a-z]+[^a-z]+|[^a-z]+[a-z]+", 0, false)] + [TestCase("HELLOOOo", 0, "(?=.{8,})[a-z]+[^a-z]+|[^a-z]+[a-z]+", 0, true)] + public void Valid_Password(string password, int minRequiredNonAlphanumericChars, string strengthRegex, int minLength, bool pass) + { + var result = MembershipProviderBase.IsPasswordValid(password, minRequiredNonAlphanumericChars, strengthRegex, minLength); + Assert.AreEqual(pass, result.Success); + } + + /// + /// The salt generated is always the same length + /// + [Test] + public void Check_Salt_Length() + { + var lastLength = 0; + for (var i = 0; i < 10000; i++) + { + var result = MembershipProviderBase.GenerateSalt(); + + if (i > 0) + { + Assert.AreEqual(lastLength, result.Length); + } + + lastLength = result.Length; + } + } + + [Test] + public void Get_StoredPassword() + { + var salt = MembershipProviderBase.GenerateSalt(); + var stored = salt + "ThisIsAHashedPassword"; + + string initSalt; + var result = MembershipProviderBase.StoredPassword(stored, MembershipPasswordFormat.Hashed, out initSalt); + + Assert.AreEqual(salt, initSalt); + } + + } +} diff --git a/src/Umbraco.Tests/Migrations/Upgrades/ValidateV7UpgradeTest.cs b/src/Umbraco.Tests/Migrations/Upgrades/ValidateV7UpgradeTest.cs index 1cdd2a0198..c1436b161d 100644 --- a/src/Umbraco.Tests/Migrations/Upgrades/ValidateV7UpgradeTest.cs +++ b/src/Umbraco.Tests/Migrations/Upgrades/ValidateV7UpgradeTest.cs @@ -1,169 +1,169 @@ -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Rdbms; -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.Caching; -using Umbraco.Core.Persistence.Migrations; -using Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven; -using Umbraco.Core.Persistence.Repositories; -using Umbraco.Core.Persistence.SqlSyntax; -using Umbraco.Core.Persistence.UnitOfWork; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Entities; - -namespace Umbraco.Tests.Migrations.Upgrades -{ - [NUnit.Framework.Ignore("This won't work because it is tested against the v6 database but once we upgrade the TagRelationshipDto to have the new cols the old cols wont exist in this test. Rest assured that this test did pass before I modified TagRelationshipDto. Just not sure how to force a legacy db to be created for a test.")] - [TestFixture] - public class ValidateV7TagsUpgradeTest : BaseDatabaseFactoryTest - { - private ContentRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository) - { - var templateRepository = new TemplateRepository(unitOfWork, NullCacheProvider.Current); - var tagRepository = new TagsRepository(unitOfWork, NullCacheProvider.Current); - contentTypeRepository = new ContentTypeRepository(unitOfWork, NullCacheProvider.Current, templateRepository); - var repository = new ContentRepository(unitOfWork, NullCacheProvider.Current, contentTypeRepository, templateRepository, tagRepository); - return repository; - } - - [Test] - public void Validate_Data_Upgrade() - { - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - ContentTypeRepository contentTypeRepository; - var insertedContent = new List(); - using (var repository = CreateRepository(unitOfWork, out contentTypeRepository)) - { - //we need to populate some data to upgrade - var contentTypeWith1Tag = MockedContentTypes.CreateSimpleContentType( - "tags1", "tags1", - new PropertyTypeCollection(new[] - { - new PropertyType("test", DataTypeDatabaseType.Ntext) {Alias = "tags1", Name = "tags1", SortOrder = 1, DataTypeDefinitionId = 1041}, - })); - var contentTypeWith2Tags = MockedContentTypes.CreateSimpleContentType( - "tags2", "tags2", - new PropertyTypeCollection(new[] - { - new PropertyType("test", DataTypeDatabaseType.Ntext) {Alias = "tags1", Name = "tags1", SortOrder = 1, DataTypeDefinitionId = 1041}, - new PropertyType("test", DataTypeDatabaseType.Ntext) {Alias = "tags2", Name = "tags2", SortOrder = 1, DataTypeDefinitionId = 1041} - })); - - contentTypeRepository.AddOrUpdate(contentTypeWith1Tag); - contentTypeRepository.AddOrUpdate(contentTypeWith2Tags); - unitOfWork.Commit(); - - for (var i = 0; i < 10; i++) - { - var content = new Content("test" + i, -1, contentTypeWith1Tag) { Language = "en-US", CreatorId = 0, WriterId = 0 }; - var obj = new - { - tags1 = "tag1,tag2,tag3,tag4,tag5" - }; - content.PropertyValues(obj); - content.ResetDirtyProperties(false); - insertedContent.Add(content); - repository.AddOrUpdate(content); - } - for (var i = 0; i < 10; i++) - { - var content = new Content("test-multi" + i, -1, contentTypeWith2Tags) { Language = "en-US", CreatorId = 0, WriterId = 0 }; - var obj = new - { - //NOTE: These will always be the same during an upgrade since we can only support tags per document not per property - tags1 = "tag1,tag2,tag3,anothertag1,anothertag2", - tags2 = "tag1,tag2,tag3,anothertag1,anothertag2" - }; - content.PropertyValues(obj); - content.ResetDirtyProperties(false); - insertedContent.Add(content); - repository.AddOrUpdate(content); - } - unitOfWork.Commit(); - } - //now that we have to create some test tag data - foreach (var tag in "tag1,tag2,tag3,tag4,tag5,anothertag1,anothertag2".Split(',')) - { - DatabaseContext.Database.Insert(new TagDto {Tag = tag, Group = "default"}); - } - var alltags = DatabaseContext.Database.Fetch("SELECT * FROM cmsTags").ToArray(); - foreach (var content in insertedContent) - { - if (content.ContentType.Alias == "tags1") - { - var tags1Tags = alltags.Where(x => "tag1,tag2,tag3,tag4,tag5".Split(',').Contains(x.Tag)); - foreach (var t in tags1Tags) - { - DatabaseContext.Database.Insert(new TagRelationshipDto {NodeId = content.Id, TagId = t.Id}); - } - } - else - { - var tags1Tags = alltags.Where(x => "tag1,tag2,tag3,anothertag1,anothertag2".Split(',').Contains(x.Tag)); - foreach (var t in tags1Tags) - { - DatabaseContext.Database.Insert(new TagRelationshipDto { NodeId = content.Id, TagId = t.Id }); - } - } - } - - //lastly, we'll insert a tag relation with a relation to only an umbracoNode - - // this will generate a delete clause and a warning - DatabaseContext.Database.Insert(new TagRelationshipDto { NodeId = -1, TagId = alltags.First().Id }); - - - var migration = new AlterTagRelationsTable(); - var migrationContext = new MigrationContext(DatabaseProviders.SqlServerCE, DatabaseContext.Database); - migration.GetUpExpressions(migrationContext); - - Assert.AreEqual( - (10 * 5) //the docs that only have 1 tag prop per document - + (10 * 5) //the docs that have 2 tag prop per document - these are the update statements - + (10 * 5) //the docs that have 2 tag prop per document - these are the insert statements - + 1//the delete clause - + 7 , //additional db expressions - migrationContext.Expressions.Count); - } - } - - [TestFixture] - public class ValidateV7UpgradeTest - { - - - [Test] - public void Validate_AddIndexToCmsMacroTable() - { - SqlSyntaxContext.SqlSyntaxProvider = new SqlCeSyntaxProvider(); - - var migration = new AddIndexToCmsMacroTable(); - var migrationContext = new MigrationContext(DatabaseProviders.SqlServerCE, null); - migration.GetUpExpressions(migrationContext); - - Assert.AreEqual(1, migrationContext.Expressions.Count); - - var result = migrationContext.Expressions.First().ToString(); - - Assert.AreEqual("CREATE UNIQUE NONCLUSTERED INDEX [IX_cmsMacro_Alias] ON [cmsMacro] ([macroAlias])", result); - } - - [Test] - public void Validate_AddIndexToCmsMacroPropertyTable() - { - SqlSyntaxContext.SqlSyntaxProvider = new SqlCeSyntaxProvider(); - - var migration = new AddIndexToCmsMacroPropertyTable(); - var migrationContext = new MigrationContext(DatabaseProviders.SqlServerCE, null); - migration.GetUpExpressions(migrationContext); - - Assert.AreEqual(1, migrationContext.Expressions.Count); - - var result = migrationContext.Expressions.First().ToString(); - - Assert.AreEqual("CREATE UNIQUE NONCLUSTERED INDEX [IX_cmsMacroProperty_Alias] ON [cmsMacroProperty] ([macro],[macroPropertyAlias])", result); - } - } +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Caching; +using Umbraco.Core.Persistence.Migrations; +using Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Entities; + +namespace Umbraco.Tests.Migrations.Upgrades +{ + [NUnit.Framework.Ignore("This won't work because it is tested against the v6 database but once we upgrade the TagRelationshipDto to have the new cols the old cols wont exist in this test. Rest assured that this test did pass before I modified TagRelationshipDto. Just not sure how to force a legacy db to be created for a test.")] + [TestFixture] + public class ValidateV7TagsUpgradeTest : BaseDatabaseFactoryTest + { + private ContentRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository) + { + var templateRepository = new TemplateRepository(unitOfWork, NullCacheProvider.Current); + var tagRepository = new TagsRepository(unitOfWork, NullCacheProvider.Current); + contentTypeRepository = new ContentTypeRepository(unitOfWork, NullCacheProvider.Current, templateRepository); + var repository = new ContentRepository(unitOfWork, NullCacheProvider.Current, contentTypeRepository, templateRepository, tagRepository); + return repository; + } + + [Test] + public void Validate_Data_Upgrade() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + ContentTypeRepository contentTypeRepository; + var insertedContent = new List(); + using (var repository = CreateRepository(unitOfWork, out contentTypeRepository)) + { + //we need to populate some data to upgrade + var contentTypeWith1Tag = MockedContentTypes.CreateSimpleContentType( + "tags1", "tags1", + new PropertyTypeCollection(new[] + { + new PropertyType("test", DataTypeDatabaseType.Ntext) {Alias = "tags1", Name = "tags1", SortOrder = 1, DataTypeDefinitionId = 1041}, + })); + var contentTypeWith2Tags = MockedContentTypes.CreateSimpleContentType( + "tags2", "tags2", + new PropertyTypeCollection(new[] + { + new PropertyType("test", DataTypeDatabaseType.Ntext) {Alias = "tags1", Name = "tags1", SortOrder = 1, DataTypeDefinitionId = 1041}, + new PropertyType("test", DataTypeDatabaseType.Ntext) {Alias = "tags2", Name = "tags2", SortOrder = 1, DataTypeDefinitionId = 1041} + })); + + contentTypeRepository.AddOrUpdate(contentTypeWith1Tag); + contentTypeRepository.AddOrUpdate(contentTypeWith2Tags); + unitOfWork.Commit(); + + for (var i = 0; i < 10; i++) + { + var content = new Content("test" + i, -1, contentTypeWith1Tag) { Language = "en-US", CreatorId = 0, WriterId = 0 }; + var obj = new + { + tags1 = "tag1,tag2,tag3,tag4,tag5" + }; + content.PropertyValues(obj); + content.ResetDirtyProperties(false); + insertedContent.Add(content); + repository.AddOrUpdate(content); + } + for (var i = 0; i < 10; i++) + { + var content = new Content("test-multi" + i, -1, contentTypeWith2Tags) { Language = "en-US", CreatorId = 0, WriterId = 0 }; + var obj = new + { + //NOTE: These will always be the same during an upgrade since we can only support tags per document not per property + tags1 = "tag1,tag2,tag3,anothertag1,anothertag2", + tags2 = "tag1,tag2,tag3,anothertag1,anothertag2" + }; + content.PropertyValues(obj); + content.ResetDirtyProperties(false); + insertedContent.Add(content); + repository.AddOrUpdate(content); + } + unitOfWork.Commit(); + } + //now that we have to create some test tag data + foreach (var tag in "tag1,tag2,tag3,tag4,tag5,anothertag1,anothertag2".Split(',')) + { + DatabaseContext.Database.Insert(new TagDto {Tag = tag, Group = "default"}); + } + var alltags = DatabaseContext.Database.Fetch("SELECT * FROM cmsTags").ToArray(); + foreach (var content in insertedContent) + { + if (content.ContentType.Alias == "tags1") + { + var tags1Tags = alltags.Where(x => "tag1,tag2,tag3,tag4,tag5".Split(',').Contains(x.Tag)); + foreach (var t in tags1Tags) + { + DatabaseContext.Database.Insert(new TagRelationshipDto {NodeId = content.Id, TagId = t.Id}); + } + } + else + { + var tags1Tags = alltags.Where(x => "tag1,tag2,tag3,anothertag1,anothertag2".Split(',').Contains(x.Tag)); + foreach (var t in tags1Tags) + { + DatabaseContext.Database.Insert(new TagRelationshipDto { NodeId = content.Id, TagId = t.Id }); + } + } + } + + //lastly, we'll insert a tag relation with a relation to only an umbracoNode - + // this will generate a delete clause and a warning + DatabaseContext.Database.Insert(new TagRelationshipDto { NodeId = -1, TagId = alltags.First().Id }); + + + var migration = new AlterTagRelationsTable(); + var migrationContext = new MigrationContext(DatabaseProviders.SqlServerCE, DatabaseContext.Database); + migration.GetUpExpressions(migrationContext); + + Assert.AreEqual( + (10 * 5) //the docs that only have 1 tag prop per document + + (10 * 5) //the docs that have 2 tag prop per document - these are the update statements + + (10 * 5) //the docs that have 2 tag prop per document - these are the insert statements + + 1//the delete clause + + 7 , //additional db expressions + migrationContext.Expressions.Count); + } + } + + [TestFixture] + public class ValidateV7UpgradeTest + { + + + [Test] + public void Validate_AddIndexToCmsMacroTable() + { + SqlSyntaxContext.SqlSyntaxProvider = new SqlCeSyntaxProvider(); + + var migration = new AddIndexToCmsMacroTable(); + var migrationContext = new MigrationContext(DatabaseProviders.SqlServerCE, null); + migration.GetUpExpressions(migrationContext); + + Assert.AreEqual(1, migrationContext.Expressions.Count); + + var result = migrationContext.Expressions.First().ToString(); + + Assert.AreEqual("CREATE UNIQUE NONCLUSTERED INDEX [IX_cmsMacro_Alias] ON [cmsMacro] ([macroAlias])", result); + } + + [Test] + public void Validate_AddIndexToCmsMacroPropertyTable() + { + SqlSyntaxContext.SqlSyntaxProvider = new SqlCeSyntaxProvider(); + + var migration = new AddIndexToCmsMacroPropertyTable(); + var migrationContext = new MigrationContext(DatabaseProviders.SqlServerCE, null); + migration.GetUpExpressions(migrationContext); + + Assert.AreEqual(1, migrationContext.Expressions.Count); + + var result = migrationContext.Expressions.First().ToString(); + + Assert.AreEqual("CREATE UNIQUE NONCLUSTERED INDEX [IX_cmsMacroProperty_Alias] ON [cmsMacroProperty] ([macro],[macroPropertyAlias])", result); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/MockTests.cs b/src/Umbraco.Tests/MockTests.cs index 52c2ee3680..f6d31a2414 100644 --- a/src/Umbraco.Tests/MockTests.cs +++ b/src/Umbraco.Tests/MockTests.cs @@ -1,130 +1,130 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web; -using NUnit.Framework; -using Umbraco.Core; -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.UnitOfWork; -using Umbraco.Core.Services; -using Moq; -using Umbraco.Web; - -namespace Umbraco.Tests -{ - [TestFixture] - public class MockTests - { - - [Test] - public void Can_Create_Empty_App_Context() - { - var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); - Assert.Pass(); - } - - [Test] - public void Can_Create_Service_Context() - { - var svcCtx = new ServiceContext( - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new PackagingService( - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new RepositoryFactory(true), - new Mock().Object), - new Mock().Object, - new RelationService( - new Mock().Object, - new RepositoryFactory(true), - new Mock().Object), - new Mock().Object, - new Mock().Object, - new Mock().Object); - Assert.Pass(); - } - - [Test] - public void Can_Create_Db_Context() - { - var dbCtx = new DatabaseContext(new Mock().Object); - Assert.Pass(); - } - - [Test] - public void Can_Create_App_Context_With_Services() - { - var appCtx = new ApplicationContext( - new DatabaseContext(new Mock().Object), - new ServiceContext( - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new PackagingService( - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new RepositoryFactory(true), - new Mock().Object), - new Mock().Object, - new RelationService( - new Mock().Object, - new RepositoryFactory(true), - new Mock().Object), - new Mock().Object, - new Mock().Object, - new Mock().Object), - CacheHelper.CreateDisabledCacheHelper()); - Assert.Pass(); - } - - [Test] - public void Can_Assign_App_Context_Singleton() - { - var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); - var result = ApplicationContext.EnsureContext(appCtx, true); - Assert.AreEqual(appCtx, result); - } - - [Test] - public void Does_Not_Overwrite_App_Context_Singleton() - { - ApplicationContext.EnsureContext(new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()), true); - var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); - var result = ApplicationContext.EnsureContext(appCtx, false); - Assert.AreNotEqual(appCtx, result); - } - - [NUnit.Framework.Ignore("Need to fix more stuff up, this is ignore because an exception occurs because it wants to ensure we have a resolver initialized - need to make that process better for testability")] - [Test] - public void Can_Get_Umbraco_Context() - { - var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); - ApplicationContext.EnsureContext(appCtx, true); - - var umbCtx = UmbracoContext.EnsureContext( - new Mock().Object, - appCtx, - true); - - Assert.AreEqual(umbCtx, UmbracoContext.Current); - } - - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Services; +using Moq; +using Umbraco.Web; + +namespace Umbraco.Tests +{ + [TestFixture] + public class MockTests + { + + [Test] + public void Can_Create_Empty_App_Context() + { + var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); + Assert.Pass(); + } + + [Test] + public void Can_Create_Service_Context() + { + var svcCtx = new ServiceContext( + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new PackagingService( + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new RepositoryFactory(true), + new Mock().Object), + new Mock().Object, + new RelationService( + new Mock().Object, + new RepositoryFactory(true), + new Mock().Object), + new Mock().Object, + new Mock().Object, + new Mock().Object); + Assert.Pass(); + } + + [Test] + public void Can_Create_Db_Context() + { + var dbCtx = new DatabaseContext(new Mock().Object); + Assert.Pass(); + } + + [Test] + public void Can_Create_App_Context_With_Services() + { + var appCtx = new ApplicationContext( + new DatabaseContext(new Mock().Object), + new ServiceContext( + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new PackagingService( + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new RepositoryFactory(true), + new Mock().Object), + new Mock().Object, + new RelationService( + new Mock().Object, + new RepositoryFactory(true), + new Mock().Object), + new Mock().Object, + new Mock().Object, + new Mock().Object), + CacheHelper.CreateDisabledCacheHelper()); + Assert.Pass(); + } + + [Test] + public void Can_Assign_App_Context_Singleton() + { + var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); + var result = ApplicationContext.EnsureContext(appCtx, true); + Assert.AreEqual(appCtx, result); + } + + [Test] + public void Does_Not_Overwrite_App_Context_Singleton() + { + ApplicationContext.EnsureContext(new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()), true); + var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); + var result = ApplicationContext.EnsureContext(appCtx, false); + Assert.AreNotEqual(appCtx, result); + } + + [NUnit.Framework.Ignore("Need to fix more stuff up, this is ignore because an exception occurs because it wants to ensure we have a resolver initialized - need to make that process better for testability")] + [Test] + public void Can_Get_Umbraco_Context() + { + var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); + ApplicationContext.EnsureContext(appCtx, true); + + var umbCtx = UmbracoContext.EnsureContext( + new Mock().Object, + appCtx, + true); + + Assert.AreEqual(umbCtx, UmbracoContext.Current); + } + + } +} diff --git a/src/Umbraco.Tests/Mvc/MergeParentContextViewDataAttributeTests.cs b/src/Umbraco.Tests/Mvc/MergeParentContextViewDataAttributeTests.cs index cd5c07c40a..2a7a05420c 100644 --- a/src/Umbraco.Tests/Mvc/MergeParentContextViewDataAttributeTests.cs +++ b/src/Umbraco.Tests/Mvc/MergeParentContextViewDataAttributeTests.cs @@ -1,86 +1,86 @@ -using System.Web.Mvc; -using System.Web.Routing; -using NUnit.Framework; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web.Mvc; - -namespace Umbraco.Tests.Mvc -{ - [TestFixture] - public class MergeParentContextViewDataAttributeTests - { - [Test] - public void Ensure_All_Ancestor_ViewData_Is_Merged() - { - var http = new FakeHttpContextFactory("http://localhost"); - - //setup an heirarchy - var rootViewCtx = new ViewContext {Controller = new MyController(), RequestContext = http.RequestContext, ViewData = new ViewDataDictionary()}; - var parentViewCtx = new ViewContext { Controller = new MyController(), RequestContext = http.RequestContext, RouteData = new RouteData(), ViewData = new ViewDataDictionary() }; - parentViewCtx.RouteData.DataTokens.Add("ParentActionViewContext", rootViewCtx); - var controllerCtx = new ControllerContext(http.RequestContext, new MyController()) {RouteData = new RouteData()}; - controllerCtx.RouteData.DataTokens.Add("ParentActionViewContext", parentViewCtx); - - //set up the view data - controllerCtx.Controller.ViewData["Test1"] = "Test1"; - controllerCtx.Controller.ViewData["Test2"] = "Test2"; - controllerCtx.Controller.ViewData["Test3"] = "Test3"; - parentViewCtx.ViewData["Test4"] = "Test4"; - parentViewCtx.ViewData["Test5"] = "Test5"; - parentViewCtx.ViewData["Test6"] = "Test6"; - rootViewCtx.ViewData["Test7"] = "Test7"; - rootViewCtx.ViewData["Test8"] = "Test8"; - rootViewCtx.ViewData["Test9"] = "Test9"; - - var filter = new ResultExecutingContext(controllerCtx, new ContentResult()) {RouteData = controllerCtx.RouteData}; - var att = new MergeParentContextViewDataAttribute(); - - Assert.IsTrue(filter.IsChildAction); - att.OnResultExecuting(filter); - - Assert.AreEqual(9, controllerCtx.Controller.ViewData.Count); - } - - [Test] - public void Ensure_All_Ancestor_ViewData_Is_Merged_Without_Data_Loss() - { - var http = new FakeHttpContextFactory("http://localhost"); - - //setup an heirarchy - var rootViewCtx = new ViewContext { Controller = new MyController(), RequestContext = http.RequestContext, ViewData = new ViewDataDictionary() }; - var parentViewCtx = new ViewContext { Controller = new MyController(), RequestContext = http.RequestContext, RouteData = new RouteData(), ViewData = new ViewDataDictionary() }; - parentViewCtx.RouteData.DataTokens.Add("ParentActionViewContext", rootViewCtx); - var controllerCtx = new ControllerContext(http.RequestContext, new MyController()) { RouteData = new RouteData() }; - controllerCtx.RouteData.DataTokens.Add("ParentActionViewContext", parentViewCtx); - - //set up the view data with overlapping keys - controllerCtx.Controller.ViewData["Test1"] = "Test1"; - controllerCtx.Controller.ViewData["Test2"] = "Test2"; - controllerCtx.Controller.ViewData["Test3"] = "Test3"; - parentViewCtx.ViewData["Test2"] = "Test4"; - parentViewCtx.ViewData["Test3"] = "Test5"; - parentViewCtx.ViewData["Test4"] = "Test6"; - rootViewCtx.ViewData["Test3"] = "Test7"; - rootViewCtx.ViewData["Test4"] = "Test8"; - rootViewCtx.ViewData["Test5"] = "Test9"; - - var filter = new ResultExecutingContext(controllerCtx, new ContentResult()) { RouteData = controllerCtx.RouteData }; - var att = new MergeParentContextViewDataAttribute(); - - Assert.IsTrue(filter.IsChildAction); - att.OnResultExecuting(filter); - - Assert.AreEqual(5, controllerCtx.Controller.ViewData.Count); - Assert.AreEqual("Test1", controllerCtx.Controller.ViewData["Test1"]); - Assert.AreEqual("Test2", controllerCtx.Controller.ViewData["Test2"]); - Assert.AreEqual("Test3", controllerCtx.Controller.ViewData["Test3"]); - Assert.AreEqual("Test6", controllerCtx.Controller.ViewData["Test4"]); - Assert.AreEqual("Test9", controllerCtx.Controller.ViewData["Test5"]); - } - - internal class MyController : Controller - { - - } - } +using System.Web.Mvc; +using System.Web.Routing; +using NUnit.Framework; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web.Mvc; + +namespace Umbraco.Tests.Mvc +{ + [TestFixture] + public class MergeParentContextViewDataAttributeTests + { + [Test] + public void Ensure_All_Ancestor_ViewData_Is_Merged() + { + var http = new FakeHttpContextFactory("http://localhost"); + + //setup an heirarchy + var rootViewCtx = new ViewContext {Controller = new MyController(), RequestContext = http.RequestContext, ViewData = new ViewDataDictionary()}; + var parentViewCtx = new ViewContext { Controller = new MyController(), RequestContext = http.RequestContext, RouteData = new RouteData(), ViewData = new ViewDataDictionary() }; + parentViewCtx.RouteData.DataTokens.Add("ParentActionViewContext", rootViewCtx); + var controllerCtx = new ControllerContext(http.RequestContext, new MyController()) {RouteData = new RouteData()}; + controllerCtx.RouteData.DataTokens.Add("ParentActionViewContext", parentViewCtx); + + //set up the view data + controllerCtx.Controller.ViewData["Test1"] = "Test1"; + controllerCtx.Controller.ViewData["Test2"] = "Test2"; + controllerCtx.Controller.ViewData["Test3"] = "Test3"; + parentViewCtx.ViewData["Test4"] = "Test4"; + parentViewCtx.ViewData["Test5"] = "Test5"; + parentViewCtx.ViewData["Test6"] = "Test6"; + rootViewCtx.ViewData["Test7"] = "Test7"; + rootViewCtx.ViewData["Test8"] = "Test8"; + rootViewCtx.ViewData["Test9"] = "Test9"; + + var filter = new ResultExecutingContext(controllerCtx, new ContentResult()) {RouteData = controllerCtx.RouteData}; + var att = new MergeParentContextViewDataAttribute(); + + Assert.IsTrue(filter.IsChildAction); + att.OnResultExecuting(filter); + + Assert.AreEqual(9, controllerCtx.Controller.ViewData.Count); + } + + [Test] + public void Ensure_All_Ancestor_ViewData_Is_Merged_Without_Data_Loss() + { + var http = new FakeHttpContextFactory("http://localhost"); + + //setup an heirarchy + var rootViewCtx = new ViewContext { Controller = new MyController(), RequestContext = http.RequestContext, ViewData = new ViewDataDictionary() }; + var parentViewCtx = new ViewContext { Controller = new MyController(), RequestContext = http.RequestContext, RouteData = new RouteData(), ViewData = new ViewDataDictionary() }; + parentViewCtx.RouteData.DataTokens.Add("ParentActionViewContext", rootViewCtx); + var controllerCtx = new ControllerContext(http.RequestContext, new MyController()) { RouteData = new RouteData() }; + controllerCtx.RouteData.DataTokens.Add("ParentActionViewContext", parentViewCtx); + + //set up the view data with overlapping keys + controllerCtx.Controller.ViewData["Test1"] = "Test1"; + controllerCtx.Controller.ViewData["Test2"] = "Test2"; + controllerCtx.Controller.ViewData["Test3"] = "Test3"; + parentViewCtx.ViewData["Test2"] = "Test4"; + parentViewCtx.ViewData["Test3"] = "Test5"; + parentViewCtx.ViewData["Test4"] = "Test6"; + rootViewCtx.ViewData["Test3"] = "Test7"; + rootViewCtx.ViewData["Test4"] = "Test8"; + rootViewCtx.ViewData["Test5"] = "Test9"; + + var filter = new ResultExecutingContext(controllerCtx, new ContentResult()) { RouteData = controllerCtx.RouteData }; + var att = new MergeParentContextViewDataAttribute(); + + Assert.IsTrue(filter.IsChildAction); + att.OnResultExecuting(filter); + + Assert.AreEqual(5, controllerCtx.Controller.ViewData.Count); + Assert.AreEqual("Test1", controllerCtx.Controller.ViewData["Test1"]); + Assert.AreEqual("Test2", controllerCtx.Controller.ViewData["Test2"]); + Assert.AreEqual("Test3", controllerCtx.Controller.ViewData["Test3"]); + Assert.AreEqual("Test6", controllerCtx.Controller.ViewData["Test4"]); + Assert.AreEqual("Test9", controllerCtx.Controller.ViewData["Test5"]); + } + + internal class MyController : Controller + { + + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs index 6887163698..c9ed53ef2a 100644 --- a/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs @@ -1,437 +1,437 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Web.Mvc; -using System.Web.Routing; -using System.Xml; -using NUnit.Framework; -using Umbraco.Web.Security; -using umbraco.BusinessLogic; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Tests.PublishedContent; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Stubs; -using Umbraco.Web; -using Umbraco.Web.Models; -using Umbraco.Web.Mvc; -using Umbraco.Web.PublishedCache; -using Umbraco.Web.PublishedCache.XmlPublishedCache; -using Umbraco.Web.Routing; - -namespace Umbraco.Tests.Mvc -{ - [TestFixture] - public class UmbracoViewPageTests - { - #region RenderModel To ... - - [Test] - public void RenderModel_To_RenderModel() - { - var content = new ContentType1(null); - var model = new RenderModel(content, CultureInfo.InvariantCulture); - var view = new RenderModelTestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.AreSame(model, view.Model); - } - - [Test] - public void RenderModel_ContentType1_To_ContentType1() - { - var content = new ContentType1(null); - var model = new RenderModel(content, CultureInfo.InvariantCulture); - var view = new ContentType1TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf(view.Model); - } - - [Test] - public void RenderModel_ContentType2_To_ContentType1() - { - var content = new ContentType2(null); - var model = new RenderModel(content, CultureInfo.InvariantCulture); - var view = new ContentType1TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf(view.Model); - } - - [Test] - public void RenderModel_ContentType1_To_ContentType2() - { - var content = new ContentType1(null); - var model = new RenderModel(content, CultureInfo.InvariantCulture); - var view = new ContentType2TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - - Assert.Throws(() => view.SetViewDataX(viewData)); - } - - [Test] - public void RenderModel_ContentType1_To_RenderModelOf_ContentType1() - { - var content = new ContentType1(null); - var model = new RenderModel(content, CultureInfo.InvariantCulture); - var view = new RenderModelOfContentType1TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf>(view.Model); - Assert.IsInstanceOf(view.Model.Content); - } - - [Test] - public void RenderModel_ContentType2_To_RenderModelOf_ContentType1() - { - var content = new ContentType2(null); - var model = new RenderModel(content, CultureInfo.InvariantCulture); - var view = new RenderModelOfContentType1TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf>(view.Model); - Assert.IsInstanceOf(view.Model.Content); - } - - [Test] - public void RenderModel_ContentType1_To_RenderModelOf_ContentType2() - { - var content = new ContentType1(null); - var model = new RenderModel(content, CultureInfo.InvariantCulture); - var view = new RenderModelOfContentType2TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - - Assert.Throws(() => view.SetViewDataX(viewData)); - } - - #endregion - - #region RenderModelOf To ... - - [Test] - public void RenderModelOf_ContentType1_To_RenderModel() - { - var content = new ContentType1(null); - var model = new RenderModel(content, CultureInfo.InvariantCulture); - var view = new RenderModelTestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.AreSame(model, view.Model); - } - - [Test] - public void RenderModelOf_ContentType1_To_ContentType1() - { - var content = new ContentType1(null); - var model = new RenderModel(content, CultureInfo.InvariantCulture); - var view = new ContentType1TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf(view.Model); - } - - [Test] - public void RenderModelOf_ContentType2_To_ContentType1() - { - var content = new ContentType2(null); - var model = new RenderModel(content, CultureInfo.InvariantCulture); - var view = new ContentType1TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf(view.Model); - } - - [Test] - public void RenderModelOf_ContentType1_To_ContentType2() - { - var content = new ContentType1(null); - var model = new RenderModel(content, CultureInfo.InvariantCulture); - var view = new ContentType2TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - Assert.Throws(() => view.SetViewDataX(viewData)); - } - - [Test] - public void RenderModelOf_ContentType1_To_RenderModelOf_ContentType1() - { - var content = new ContentType1(null); - var model = new RenderModel(content, CultureInfo.InvariantCulture); - var view = new RenderModelOfContentType1TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf>(view.Model); - Assert.IsInstanceOf(view.Model.Content); - } - - [Test] - public void RenderModelOf_ContentType2_To_RenderModelOf_ContentType1() - { - var content = new ContentType2(null); - var model = new RenderModel(content, CultureInfo.InvariantCulture); - var view = new RenderModelOfContentType1TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf>(view.Model); - Assert.IsInstanceOf(view.Model.Content); - } - - [Test] - public void RenderModelOf_ContentType1_To_RenderModelOf_ContentType2() - { - var content = new ContentType1(null); - var model = new RenderModel(content, CultureInfo.InvariantCulture); - var view = new RenderModelOfContentType2TestPage(); - var viewData = new ViewDataDictionary(model); - - view.ViewContext = GetViewContext(); - Assert.Throws(() => view.SetViewDataX(viewData)); - } - - #endregion - - #region ContentType To ... - - [Test] - public void ContentType1_To_RenderModel() - { - var content = new ContentType1(null); - var view = new RenderModelTestPage(); - var viewData = new ViewDataDictionary(content); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf(view.Model); - } - - [Test] - public void ContentType1_To_RenderModelOf_ContentType1() - { - var content = new ContentType1(null); - var view = new RenderModelOfContentType1TestPage(); - var viewData = new ViewDataDictionary(content); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf>(view.Model); - Assert.IsInstanceOf(view.Model.Content); - } - - [Test] - public void ContentType2_To_RenderModelOf_ContentType1() - { - var content = new ContentType2(null); - var view = new RenderModelOfContentType1TestPage(); - var viewData = new ViewDataDictionary(content); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf>(view.Model); - Assert.IsInstanceOf(view.Model.Content); - } - - [Test] - public void ContentType1_To_RenderModelOf_ContentType2() - { - var content = new ContentType1(null); - var view = new RenderModelOfContentType2TestPage(); - var viewData = new ViewDataDictionary(content); - - view.ViewContext = GetViewContext(); - Assert.Throws(() =>view.SetViewDataX(viewData)); - } - - [Test] - public void ContentType1_To_ContentType1() - { - var content = new ContentType1(null); - var view = new ContentType1TestPage(); - var viewData = new ViewDataDictionary(content); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf(view.Model); - } - - [Test] - public void ContentType1_To_ContentType2() - { - var content = new ContentType1(null); - var view = new ContentType2TestPage(); - var viewData = new ViewDataDictionary(content); - - view.ViewContext = GetViewContext(); - Assert.Throws(() => view.SetViewDataX(viewData)); - } - - [Test] - public void ContentType2_To_ContentType1() - { - var content = new ContentType2(null); - var view = new ContentType1TestPage(); - var viewData = new ViewDataDictionary(content); - - view.ViewContext = GetViewContext(); - view.SetViewDataX(viewData); - - Assert.IsInstanceOf(view.Model); - } - - #endregion - - #region Test elements - - public class TestPage : UmbracoViewPage - { - public override void Execute() - { - throw new NotImplementedException(); - } - - public void SetViewDataX(ViewDataDictionary viewData) - { - SetViewData(viewData); - } - } - - public class RenderModelTestPage : TestPage - { } - - public class RenderModelOfContentType1TestPage : TestPage> - { } - - public class RenderModelOfContentType2TestPage : TestPage> - { } - - public class ContentType1TestPage : TestPage - { } - - public class ContentType2TestPage : TestPage - { } - - public class ContentType1 : PublishedContentWrapped - { - public ContentType1(IPublishedContent content) : base(content) {} - } - - public class ContentType2 : ContentType1 - { - public ContentType2(IPublishedContent content) : base(content) { } - } - - #endregion - - #region Test helpers - - ViewContext GetViewContext() - { - var umbracoContext = GetUmbracoContext("/dang", 0); - - var urlProvider = new UrlProvider(umbracoContext, new IUrlProvider[] { new DefaultUrlProvider() }); - var routingContext = new RoutingContext( - umbracoContext, - Enumerable.Empty(), - new FakeLastChanceFinder(), - urlProvider); - umbracoContext.RoutingContext = routingContext; - - var request = new PublishedContentRequest(new Uri("http://localhost/dang"), routingContext); - request.Culture = CultureInfo.InvariantCulture; - umbracoContext.PublishedContentRequest = request; - - var context = new ViewContext(); - context.RouteData = new RouteData(); - context.RouteData.DataTokens.Add("umbraco-context", umbracoContext); - - return context; - } - - protected UmbracoContext GetUmbracoContext(string url, int templateId, RouteData routeData = null, bool setSingleton = false) - { - var cache = new PublishedContentCache(); - - //cache.GetXmlDelegate = (context, preview) => - //{ - // var doc = new XmlDocument(); - // doc.LoadXml(GetXmlContent(templateId)); - // return doc; - //}; - - //PublishedContentCache.UnitTesting = true; - - // ApplicationContext.Current = new ApplicationContext(false) { IsReady = true }; - var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()) { IsReady = true }; - var http = GetHttpContextFactory(url, routeData).HttpContext; - var ctx = new UmbracoContext( - GetHttpContextFactory(url, routeData).HttpContext, - appCtx, - new PublishedCaches(cache, new PublishedMediaCache()), - new WebSecurity(http, appCtx)); - - //if (setSingleton) - //{ - // UmbracoContext.Current = ctx; - //} - - return ctx; - } - - protected FakeHttpContextFactory GetHttpContextFactory(string url, RouteData routeData = null) - { - var factory = routeData != null - ? new FakeHttpContextFactory(url, routeData) - : new FakeHttpContextFactory(url); - - - //set the state helper - StateHelper.HttpContext = factory.HttpContext; - - return factory; - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Web.Mvc; +using System.Web.Routing; +using System.Xml; +using NUnit.Framework; +using Umbraco.Web.Security; +using umbraco.BusinessLogic; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Tests.PublishedContent; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Stubs; +using Umbraco.Web; +using Umbraco.Web.Models; +using Umbraco.Web.Mvc; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.PublishedCache.XmlPublishedCache; +using Umbraco.Web.Routing; + +namespace Umbraco.Tests.Mvc +{ + [TestFixture] + public class UmbracoViewPageTests + { + #region RenderModel To ... + + [Test] + public void RenderModel_To_RenderModel() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new RenderModelTestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.AreSame(model, view.Model); + } + + [Test] + public void RenderModel_ContentType1_To_ContentType1() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new ContentType1TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf(view.Model); + } + + [Test] + public void RenderModel_ContentType2_To_ContentType1() + { + var content = new ContentType2(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new ContentType1TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf(view.Model); + } + + [Test] + public void RenderModel_ContentType1_To_ContentType2() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new ContentType2TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + + Assert.Throws(() => view.SetViewDataX(viewData)); + } + + [Test] + public void RenderModel_ContentType1_To_RenderModelOf_ContentType1() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new RenderModelOfContentType1TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf>(view.Model); + Assert.IsInstanceOf(view.Model.Content); + } + + [Test] + public void RenderModel_ContentType2_To_RenderModelOf_ContentType1() + { + var content = new ContentType2(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new RenderModelOfContentType1TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf>(view.Model); + Assert.IsInstanceOf(view.Model.Content); + } + + [Test] + public void RenderModel_ContentType1_To_RenderModelOf_ContentType2() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new RenderModelOfContentType2TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + + Assert.Throws(() => view.SetViewDataX(viewData)); + } + + #endregion + + #region RenderModelOf To ... + + [Test] + public void RenderModelOf_ContentType1_To_RenderModel() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new RenderModelTestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.AreSame(model, view.Model); + } + + [Test] + public void RenderModelOf_ContentType1_To_ContentType1() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new ContentType1TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf(view.Model); + } + + [Test] + public void RenderModelOf_ContentType2_To_ContentType1() + { + var content = new ContentType2(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new ContentType1TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf(view.Model); + } + + [Test] + public void RenderModelOf_ContentType1_To_ContentType2() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new ContentType2TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + Assert.Throws(() => view.SetViewDataX(viewData)); + } + + [Test] + public void RenderModelOf_ContentType1_To_RenderModelOf_ContentType1() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new RenderModelOfContentType1TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf>(view.Model); + Assert.IsInstanceOf(view.Model.Content); + } + + [Test] + public void RenderModelOf_ContentType2_To_RenderModelOf_ContentType1() + { + var content = new ContentType2(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new RenderModelOfContentType1TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf>(view.Model); + Assert.IsInstanceOf(view.Model.Content); + } + + [Test] + public void RenderModelOf_ContentType1_To_RenderModelOf_ContentType2() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new RenderModelOfContentType2TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + Assert.Throws(() => view.SetViewDataX(viewData)); + } + + #endregion + + #region ContentType To ... + + [Test] + public void ContentType1_To_RenderModel() + { + var content = new ContentType1(null); + var view = new RenderModelTestPage(); + var viewData = new ViewDataDictionary(content); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf(view.Model); + } + + [Test] + public void ContentType1_To_RenderModelOf_ContentType1() + { + var content = new ContentType1(null); + var view = new RenderModelOfContentType1TestPage(); + var viewData = new ViewDataDictionary(content); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf>(view.Model); + Assert.IsInstanceOf(view.Model.Content); + } + + [Test] + public void ContentType2_To_RenderModelOf_ContentType1() + { + var content = new ContentType2(null); + var view = new RenderModelOfContentType1TestPage(); + var viewData = new ViewDataDictionary(content); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf>(view.Model); + Assert.IsInstanceOf(view.Model.Content); + } + + [Test] + public void ContentType1_To_RenderModelOf_ContentType2() + { + var content = new ContentType1(null); + var view = new RenderModelOfContentType2TestPage(); + var viewData = new ViewDataDictionary(content); + + view.ViewContext = GetViewContext(); + Assert.Throws(() =>view.SetViewDataX(viewData)); + } + + [Test] + public void ContentType1_To_ContentType1() + { + var content = new ContentType1(null); + var view = new ContentType1TestPage(); + var viewData = new ViewDataDictionary(content); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf(view.Model); + } + + [Test] + public void ContentType1_To_ContentType2() + { + var content = new ContentType1(null); + var view = new ContentType2TestPage(); + var viewData = new ViewDataDictionary(content); + + view.ViewContext = GetViewContext(); + Assert.Throws(() => view.SetViewDataX(viewData)); + } + + [Test] + public void ContentType2_To_ContentType1() + { + var content = new ContentType2(null); + var view = new ContentType1TestPage(); + var viewData = new ViewDataDictionary(content); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf(view.Model); + } + + #endregion + + #region Test elements + + public class TestPage : UmbracoViewPage + { + public override void Execute() + { + throw new NotImplementedException(); + } + + public void SetViewDataX(ViewDataDictionary viewData) + { + SetViewData(viewData); + } + } + + public class RenderModelTestPage : TestPage + { } + + public class RenderModelOfContentType1TestPage : TestPage> + { } + + public class RenderModelOfContentType2TestPage : TestPage> + { } + + public class ContentType1TestPage : TestPage + { } + + public class ContentType2TestPage : TestPage + { } + + public class ContentType1 : PublishedContentWrapped + { + public ContentType1(IPublishedContent content) : base(content) {} + } + + public class ContentType2 : ContentType1 + { + public ContentType2(IPublishedContent content) : base(content) { } + } + + #endregion + + #region Test helpers + + ViewContext GetViewContext() + { + var umbracoContext = GetUmbracoContext("/dang", 0); + + var urlProvider = new UrlProvider(umbracoContext, new IUrlProvider[] { new DefaultUrlProvider() }); + var routingContext = new RoutingContext( + umbracoContext, + Enumerable.Empty(), + new FakeLastChanceFinder(), + urlProvider); + umbracoContext.RoutingContext = routingContext; + + var request = new PublishedContentRequest(new Uri("http://localhost/dang"), routingContext); + request.Culture = CultureInfo.InvariantCulture; + umbracoContext.PublishedContentRequest = request; + + var context = new ViewContext(); + context.RouteData = new RouteData(); + context.RouteData.DataTokens.Add("umbraco-context", umbracoContext); + + return context; + } + + protected UmbracoContext GetUmbracoContext(string url, int templateId, RouteData routeData = null, bool setSingleton = false) + { + var cache = new PublishedContentCache(); + + //cache.GetXmlDelegate = (context, preview) => + //{ + // var doc = new XmlDocument(); + // doc.LoadXml(GetXmlContent(templateId)); + // return doc; + //}; + + //PublishedContentCache.UnitTesting = true; + + // ApplicationContext.Current = new ApplicationContext(false) { IsReady = true }; + var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()) { IsReady = true }; + var http = GetHttpContextFactory(url, routeData).HttpContext; + var ctx = new UmbracoContext( + GetHttpContextFactory(url, routeData).HttpContext, + appCtx, + new PublishedCaches(cache, new PublishedMediaCache()), + new WebSecurity(http, appCtx)); + + //if (setSingleton) + //{ + // UmbracoContext.Current = ctx; + //} + + return ctx; + } + + protected FakeHttpContextFactory GetHttpContextFactory(string url, RouteData routeData = null) + { + var factory = routeData != null + ? new FakeHttpContextFactory(url, routeData) + : new FakeHttpContextFactory(url); + + + //set the state helper + StateHelper.HttpContext = factory.HttpContext; + + return factory; + } + + #endregion + } +} diff --git a/src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs b/src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs index 2f35aede2d..c13818a03c 100644 --- a/src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs +++ b/src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs @@ -1,44 +1,44 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web.Mvc; -using NUnit.Framework; -using Umbraco.Web.Mvc; - -namespace Umbraco.Tests.Mvc -{ - [TestFixture] - public class ViewDataDictionaryExtensionTests - { - [Test] - public void Merge_View_Data() - { - var source = new ViewDataDictionary(); - var dest = new ViewDataDictionary(); - source.Add("Test1", "Test1"); - dest.Add("Test2", "Test2"); - - dest.MergeViewDataFrom(source); - - Assert.AreEqual(2, dest.Count); - } - - [Test] - public void Merge_View_Data_Retains_Destination_Values() - { - var source = new ViewDataDictionary(); - var dest = new ViewDataDictionary(); - source.Add("Test1", "Test1"); - dest.Add("Test1", "MyValue"); - dest.Add("Test2", "Test2"); - - dest.MergeViewDataFrom(source); - - Assert.AreEqual(2, dest.Count); - Assert.AreEqual("MyValue", dest["Test1"]); - Assert.AreEqual("Test2", dest["Test2"]); - } - - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web.Mvc; +using NUnit.Framework; +using Umbraco.Web.Mvc; + +namespace Umbraco.Tests.Mvc +{ + [TestFixture] + public class ViewDataDictionaryExtensionTests + { + [Test] + public void Merge_View_Data() + { + var source = new ViewDataDictionary(); + var dest = new ViewDataDictionary(); + source.Add("Test1", "Test1"); + dest.Add("Test2", "Test2"); + + dest.MergeViewDataFrom(source); + + Assert.AreEqual(2, dest.Count); + } + + [Test] + public void Merge_View_Data_Retains_Destination_Values() + { + var source = new ViewDataDictionary(); + var dest = new ViewDataDictionary(); + source.Add("Test1", "Test1"); + dest.Add("Test1", "MyValue"); + dest.Add("Test2", "Test2"); + + dest.MergeViewDataFrom(source); + + Assert.AreEqual(2, dest.Count); + Assert.AreEqual("MyValue", dest["Test1"]); + Assert.AreEqual("Test2", dest["Test2"]); + } + + } +} diff --git a/src/Umbraco.Tests/Persistence/Auditing/AuditTests.cs b/src/Umbraco.Tests/Persistence/Auditing/AuditTests.cs index 68e7bcb003..f8ba631e43 100644 --- a/src/Umbraco.Tests/Persistence/Auditing/AuditTests.cs +++ b/src/Umbraco.Tests/Persistence/Auditing/AuditTests.cs @@ -1,35 +1,35 @@ -using System.Linq; -using NUnit.Framework; -using Umbraco.Core.Auditing; -using Umbraco.Core.Models.Rdbms; -using Umbraco.Tests.TestHelpers; - -namespace Umbraco.Tests.Persistence.Auditing -{ - [TestFixture] - public class AuditTests : BaseDatabaseFactoryTest - { - [SetUp] - public override void Initialize() - { - base.Initialize(); - } - - [Test] - public void Can_Add_Audit_Entry() - { - Audit.Add(AuditTypes.System, "This is a System audit trail", 0, -1); - - var dtos = DatabaseContext.Database.Fetch("WHERE id > -1"); - - Assert.That(dtos.Any(), Is.True); - Assert.That(dtos.First().Comment, Is.EqualTo("This is a System audit trail")); - } - - [TearDown] - public override void TearDown() - { - base.TearDown(); - } - } +using System.Linq; +using NUnit.Framework; +using Umbraco.Core.Auditing; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Tests.TestHelpers; + +namespace Umbraco.Tests.Persistence.Auditing +{ + [TestFixture] + public class AuditTests : BaseDatabaseFactoryTest + { + [SetUp] + public override void Initialize() + { + base.Initialize(); + } + + [Test] + public void Can_Add_Audit_Entry() + { + Audit.Add(AuditTypes.System, "This is a System audit trail", 0, -1); + + var dtos = DatabaseContext.Database.Fetch("WHERE id > -1"); + + Assert.That(dtos.Any(), Is.True); + Assert.That(dtos.First().Comment, Is.EqualTo("This is a System audit trail")); + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs index 9921d957e4..4af1ff0279 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs @@ -1,388 +1,388 @@ -using System.Data.SqlServerCe; -using System.Linq; -using NUnit.Framework; -using Umbraco.Core.Models; -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.Caching; -using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.Repositories; -using Umbraco.Core.Persistence.UnitOfWork; -using Umbraco.Tests.TestHelpers; - -namespace Umbraco.Tests.Persistence.Repositories -{ - [TestFixture] - public class MacroRepositoryTest : BaseDatabaseFactoryTest - { - [SetUp] - public override void Initialize() - { - base.Initialize(); - - CreateTestData(); - } - - [Test] - public void Cannot_Add_Duplicate_Macros() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - - // Act - using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) - { - var macro = new Macro("test1", "Test", "~/usercontrol/blah.ascx", "MyAssembly", "test.xslt", "~/views/macropartials/test.cshtml"); - repository.AddOrUpdate(macro); - - Assert.Throws(unitOfWork.Commit); - } - - } - - [Test] - public void Cannot_Update_To_Duplicate_Macro_Alias() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - - // Act - using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) - { - var macro = repository.Get(1); - macro.Alias = "test2"; - repository.AddOrUpdate(macro); - Assert.Throws(unitOfWork.Commit); - } - - } - - [Test] - public void Can_Instantiate_Repository() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - - // Act - using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) - { - // Assert - Assert.That(repository, Is.Not.Null); - } - } - - [Test] - public void Can_Perform_Get_On_Repository() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) - { - // Act - var macro = repository.Get(1); - - // Assert - Assert.That(macro, Is.Not.Null); - Assert.That(macro.HasIdentity, Is.True); - Assert.That(macro.Alias, Is.EqualTo("test1")); - Assert.That(macro.CacheByPage, Is.EqualTo(false)); - Assert.That(macro.CacheByMember, Is.EqualTo(false)); - Assert.That(macro.ControlAssembly, Is.EqualTo("MyAssembly1")); - Assert.That(macro.ControlType, Is.EqualTo("~/usercontrol/test1.ascx")); - Assert.That(macro.DontRender, Is.EqualTo(true)); - Assert.That(macro.Name, Is.EqualTo("Test1")); - Assert.That(macro.CacheDuration, Is.EqualTo(0)); - Assert.That(macro.ScriptPath, Is.EqualTo("~/views/macropartials/test1.cshtml")); - Assert.That(macro.UseInEditor, Is.EqualTo(false)); - Assert.That(macro.XsltPath, Is.EqualTo("test1.xslt")); - } - - - } - - [Test] - public void Can_Perform_GetAll_On_Repository() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) - { - // Act - var macros = repository.GetAll(); - - // Assert - Assert.That(macros.Count(), Is.EqualTo(3)); - } - - } - - [Test] - public void Can_Perform_GetByQuery_On_Repository() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) - { - // Act - var query = Query.Builder.Where(x => x.Alias.ToUpper() == "TEST1"); - var result = repository.GetByQuery(query); - - // Assert - Assert.AreEqual(1, result.Count()); - } - } - - [Test] - public void Can_Perform_Count_On_Repository() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) - { - // Act - var query = Query.Builder.Where(x => x.Name.StartsWith("Test")); - int count = repository.Count(query); - - // Assert - Assert.That(count, Is.EqualTo(2)); - } - } - - [Test] - public void Can_Perform_Add_On_Repository() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) - { - // Act - var macro = new Macro("test", "Test", "~/usercontrol/blah.ascx", "MyAssembly", "test.xslt", "~/views/macropartials/test.cshtml"); - macro.Properties.Add(new MacroProperty("test", "Test", 0, "test")); - repository.AddOrUpdate(macro); - unitOfWork.Commit(); - - // Assert - Assert.That(macro.HasIdentity, Is.True); - Assert.That(macro.Id, Is.EqualTo(4));//With 3 existing entries the Id should be 4 - Assert.Greater(macro.Properties.Single().Id, 0); - } - } - - [Test] - public void Can_Perform_Update_On_Repository() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) - { - // Act - var macro = repository.Get(2); - macro.Name = "Hello"; - macro.CacheDuration = 1234; - macro.CacheByPage = true; - macro.CacheByMember = true; - macro.ControlAssembly = ""; - macro.ControlType = ""; - macro.DontRender = false; - macro.ScriptPath = "~/newpath.cshtml"; - macro.UseInEditor = true; - macro.XsltPath = ""; - - repository.AddOrUpdate(macro); - unitOfWork.Commit(); - - var macroUpdated = repository.Get(2); - - // Assert - Assert.That(macroUpdated, Is.Not.Null); - Assert.That(macroUpdated.Name, Is.EqualTo("Hello")); - Assert.That(macroUpdated.CacheDuration, Is.EqualTo(1234)); - Assert.That(macroUpdated.CacheByPage, Is.EqualTo(true)); - Assert.That(macroUpdated.CacheByMember, Is.EqualTo(true)); - Assert.That(macroUpdated.ControlAssembly, Is.EqualTo("")); - Assert.That(macroUpdated.ControlType, Is.EqualTo("")); - Assert.That(macroUpdated.DontRender, Is.EqualTo(false)); - Assert.That(macroUpdated.ScriptPath, Is.EqualTo("~/newpath.cshtml")); - Assert.That(macroUpdated.UseInEditor, Is.EqualTo(true)); - Assert.That(macroUpdated.XsltPath, Is.EqualTo("")); - } - } - - [Test] - public void Can_Perform_Delete_On_Repository() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) - { - // Act - var macro = repository.Get(3); - Assert.IsNotNull(macro); - repository.Delete(macro); - unitOfWork.Commit(); - - var exists = repository.Exists(3); - - // Assert - Assert.That(exists, Is.False); - } - } - - [Test] - public void Can_Perform_Exists_On_Repository() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) - { - // Act - var exists = repository.Exists(3); - var doesntExist = repository.Exists(10); - - // Assert - Assert.That(exists, Is.True); - Assert.That(doesntExist, Is.False); - } - } - - [Test] - public void Can_Add_Property_For_Macro() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) - { - var macro = repository.Get(1); - macro.Properties.Add(new MacroProperty("new1", "New1", 3, "test")); - - repository.AddOrUpdate(macro); - - unitOfWork.Commit(); - - // Assert - Assert.Greater(macro.Properties.First().Id, 0); //ensure id is returned - var result = repository.Get(1); - Assert.Greater(result.Properties.First().Id, 0); - Assert.AreEqual(1, result.Properties.Count()); - Assert.AreEqual("new1", result.Properties.First().Alias); - Assert.AreEqual("New1", result.Properties.First().Name); - Assert.AreEqual(3, result.Properties.First().SortOrder); - - } - } - - [Test] - public void Can_Add_New_Macro_With_Property() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) - { - var macro = new Macro("newmacro", "A new macro", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml"); - macro.Properties.Add(new MacroProperty("blah1", "New1", 4, "test.editor")); - - repository.AddOrUpdate(macro); - - unitOfWork.Commit(); - - // Assert - var result = repository.Get(macro.Id); - Assert.AreEqual(1, result.Properties.Count()); - Assert.AreEqual("blah1", result.Properties.First().Alias); - Assert.AreEqual("New1", result.Properties.First().Name); - Assert.AreEqual(4, result.Properties.First().SortOrder); - - } - } - - [Test] - public void Can_Remove_Macro_Property() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) - { - var macro = new Macro("newmacro", "A new macro", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml"); - macro.Properties.Add(new MacroProperty("blah1", "New1", 4, "test.editor")); - repository.AddOrUpdate(macro); - unitOfWork.Commit(); - - var result = repository.Get(macro.Id); - result.Properties.Remove("blah1"); - repository.AddOrUpdate(result); - unitOfWork.Commit(); - - // Assert - result = repository.Get(macro.Id); - Assert.AreEqual(0, result.Properties.Count()); - - } - } - - [Test] - public void Can_Add_Remove_Macro_Properties() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) - { - var macro = new Macro("newmacro", "A new macro", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml"); - var prop1 = new MacroProperty("blah1", "New1", 4, "test.editor"); - var prop2 = new MacroProperty("blah2", "New2", 3, "test.editor"); - - //add/remove a few to test the collection observable - macro.Properties.Add(prop1); - macro.Properties.Add(prop2); - macro.Properties.Remove(prop1); - macro.Properties.Remove("blah2"); - macro.Properties.Add(prop2); - - repository.AddOrUpdate(macro); - unitOfWork.Commit(); - - // Assert - var result = repository.Get(macro.Id); - - Assert.AreEqual(1, result.Properties.Count()); - Assert.AreEqual("blah2", result.Properties.Single().Alias); - - } - } - - - - [TearDown] - public override void TearDown() - { - base.TearDown(); - } - - public void CreateTestData() - { - var provider = new PetaPocoUnitOfWorkProvider(); - using (var unitOfWork = provider.GetUnitOfWork()) - using (var repository = new MacroRepository(unitOfWork)) - { - repository.AddOrUpdate(new Macro("test1", "Test1", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml")); - repository.AddOrUpdate(new Macro("test2", "Test2", "~/usercontrol/test2.ascx", "MyAssembly2", "test2.xslt", "~/views/macropartials/test2.cshtml")); - repository.AddOrUpdate(new Macro("test3", "Tet3", "~/usercontrol/test3.ascx", "MyAssembly3", "test3.xslt", "~/views/macropartials/test3.cshtml")); - unitOfWork.Commit(); - } - - } - } +using System.Data.SqlServerCe; +using System.Linq; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Caching; +using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Tests.TestHelpers; + +namespace Umbraco.Tests.Persistence.Repositories +{ + [TestFixture] + public class MacroRepositoryTest : BaseDatabaseFactoryTest + { + [SetUp] + public override void Initialize() + { + base.Initialize(); + + CreateTestData(); + } + + [Test] + public void Cannot_Add_Duplicate_Macros() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + + // Act + using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) + { + var macro = new Macro("test1", "Test", "~/usercontrol/blah.ascx", "MyAssembly", "test.xslt", "~/views/macropartials/test.cshtml"); + repository.AddOrUpdate(macro); + + Assert.Throws(unitOfWork.Commit); + } + + } + + [Test] + public void Cannot_Update_To_Duplicate_Macro_Alias() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + + // Act + using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) + { + var macro = repository.Get(1); + macro.Alias = "test2"; + repository.AddOrUpdate(macro); + Assert.Throws(unitOfWork.Commit); + } + + } + + [Test] + public void Can_Instantiate_Repository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + + // Act + using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) + { + // Assert + Assert.That(repository, Is.Not.Null); + } + } + + [Test] + public void Can_Perform_Get_On_Repository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) + { + // Act + var macro = repository.Get(1); + + // Assert + Assert.That(macro, Is.Not.Null); + Assert.That(macro.HasIdentity, Is.True); + Assert.That(macro.Alias, Is.EqualTo("test1")); + Assert.That(macro.CacheByPage, Is.EqualTo(false)); + Assert.That(macro.CacheByMember, Is.EqualTo(false)); + Assert.That(macro.ControlAssembly, Is.EqualTo("MyAssembly1")); + Assert.That(macro.ControlType, Is.EqualTo("~/usercontrol/test1.ascx")); + Assert.That(macro.DontRender, Is.EqualTo(true)); + Assert.That(macro.Name, Is.EqualTo("Test1")); + Assert.That(macro.CacheDuration, Is.EqualTo(0)); + Assert.That(macro.ScriptPath, Is.EqualTo("~/views/macropartials/test1.cshtml")); + Assert.That(macro.UseInEditor, Is.EqualTo(false)); + Assert.That(macro.XsltPath, Is.EqualTo("test1.xslt")); + } + + + } + + [Test] + public void Can_Perform_GetAll_On_Repository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) + { + // Act + var macros = repository.GetAll(); + + // Assert + Assert.That(macros.Count(), Is.EqualTo(3)); + } + + } + + [Test] + public void Can_Perform_GetByQuery_On_Repository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) + { + // Act + var query = Query.Builder.Where(x => x.Alias.ToUpper() == "TEST1"); + var result = repository.GetByQuery(query); + + // Assert + Assert.AreEqual(1, result.Count()); + } + } + + [Test] + public void Can_Perform_Count_On_Repository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) + { + // Act + var query = Query.Builder.Where(x => x.Name.StartsWith("Test")); + int count = repository.Count(query); + + // Assert + Assert.That(count, Is.EqualTo(2)); + } + } + + [Test] + public void Can_Perform_Add_On_Repository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) + { + // Act + var macro = new Macro("test", "Test", "~/usercontrol/blah.ascx", "MyAssembly", "test.xslt", "~/views/macropartials/test.cshtml"); + macro.Properties.Add(new MacroProperty("test", "Test", 0, "test")); + repository.AddOrUpdate(macro); + unitOfWork.Commit(); + + // Assert + Assert.That(macro.HasIdentity, Is.True); + Assert.That(macro.Id, Is.EqualTo(4));//With 3 existing entries the Id should be 4 + Assert.Greater(macro.Properties.Single().Id, 0); + } + } + + [Test] + public void Can_Perform_Update_On_Repository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) + { + // Act + var macro = repository.Get(2); + macro.Name = "Hello"; + macro.CacheDuration = 1234; + macro.CacheByPage = true; + macro.CacheByMember = true; + macro.ControlAssembly = ""; + macro.ControlType = ""; + macro.DontRender = false; + macro.ScriptPath = "~/newpath.cshtml"; + macro.UseInEditor = true; + macro.XsltPath = ""; + + repository.AddOrUpdate(macro); + unitOfWork.Commit(); + + var macroUpdated = repository.Get(2); + + // Assert + Assert.That(macroUpdated, Is.Not.Null); + Assert.That(macroUpdated.Name, Is.EqualTo("Hello")); + Assert.That(macroUpdated.CacheDuration, Is.EqualTo(1234)); + Assert.That(macroUpdated.CacheByPage, Is.EqualTo(true)); + Assert.That(macroUpdated.CacheByMember, Is.EqualTo(true)); + Assert.That(macroUpdated.ControlAssembly, Is.EqualTo("")); + Assert.That(macroUpdated.ControlType, Is.EqualTo("")); + Assert.That(macroUpdated.DontRender, Is.EqualTo(false)); + Assert.That(macroUpdated.ScriptPath, Is.EqualTo("~/newpath.cshtml")); + Assert.That(macroUpdated.UseInEditor, Is.EqualTo(true)); + Assert.That(macroUpdated.XsltPath, Is.EqualTo("")); + } + } + + [Test] + public void Can_Perform_Delete_On_Repository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) + { + // Act + var macro = repository.Get(3); + Assert.IsNotNull(macro); + repository.Delete(macro); + unitOfWork.Commit(); + + var exists = repository.Exists(3); + + // Assert + Assert.That(exists, Is.False); + } + } + + [Test] + public void Can_Perform_Exists_On_Repository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) + { + // Act + var exists = repository.Exists(3); + var doesntExist = repository.Exists(10); + + // Assert + Assert.That(exists, Is.True); + Assert.That(doesntExist, Is.False); + } + } + + [Test] + public void Can_Add_Property_For_Macro() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) + { + var macro = repository.Get(1); + macro.Properties.Add(new MacroProperty("new1", "New1", 3, "test")); + + repository.AddOrUpdate(macro); + + unitOfWork.Commit(); + + // Assert + Assert.Greater(macro.Properties.First().Id, 0); //ensure id is returned + var result = repository.Get(1); + Assert.Greater(result.Properties.First().Id, 0); + Assert.AreEqual(1, result.Properties.Count()); + Assert.AreEqual("new1", result.Properties.First().Alias); + Assert.AreEqual("New1", result.Properties.First().Name); + Assert.AreEqual(3, result.Properties.First().SortOrder); + + } + } + + [Test] + public void Can_Add_New_Macro_With_Property() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) + { + var macro = new Macro("newmacro", "A new macro", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml"); + macro.Properties.Add(new MacroProperty("blah1", "New1", 4, "test.editor")); + + repository.AddOrUpdate(macro); + + unitOfWork.Commit(); + + // Assert + var result = repository.Get(macro.Id); + Assert.AreEqual(1, result.Properties.Count()); + Assert.AreEqual("blah1", result.Properties.First().Alias); + Assert.AreEqual("New1", result.Properties.First().Name); + Assert.AreEqual(4, result.Properties.First().SortOrder); + + } + } + + [Test] + public void Can_Remove_Macro_Property() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) + { + var macro = new Macro("newmacro", "A new macro", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml"); + macro.Properties.Add(new MacroProperty("blah1", "New1", 4, "test.editor")); + repository.AddOrUpdate(macro); + unitOfWork.Commit(); + + var result = repository.Get(macro.Id); + result.Properties.Remove("blah1"); + repository.AddOrUpdate(result); + unitOfWork.Commit(); + + // Assert + result = repository.Get(macro.Id); + Assert.AreEqual(0, result.Properties.Count()); + + } + } + + [Test] + public void Can_Add_Remove_Macro_Properties() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current)) + { + var macro = new Macro("newmacro", "A new macro", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml"); + var prop1 = new MacroProperty("blah1", "New1", 4, "test.editor"); + var prop2 = new MacroProperty("blah2", "New2", 3, "test.editor"); + + //add/remove a few to test the collection observable + macro.Properties.Add(prop1); + macro.Properties.Add(prop2); + macro.Properties.Remove(prop1); + macro.Properties.Remove("blah2"); + macro.Properties.Add(prop2); + + repository.AddOrUpdate(macro); + unitOfWork.Commit(); + + // Assert + var result = repository.Get(macro.Id); + + Assert.AreEqual(1, result.Properties.Count()); + Assert.AreEqual("blah2", result.Properties.Single().Alias); + + } + } + + + + [TearDown] + public override void TearDown() + { + base.TearDown(); + } + + public void CreateTestData() + { + var provider = new PetaPocoUnitOfWorkProvider(); + using (var unitOfWork = provider.GetUnitOfWork()) + using (var repository = new MacroRepository(unitOfWork)) + { + repository.AddOrUpdate(new Macro("test1", "Test1", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml")); + repository.AddOrUpdate(new Macro("test2", "Test2", "~/usercontrol/test2.ascx", "MyAssembly2", "test2.xslt", "~/views/macropartials/test2.cshtml")); + repository.AddOrUpdate(new Macro("test3", "Tet3", "~/usercontrol/test3.ascx", "MyAssembly3", "test3.xslt", "~/views/macropartials/test3.cshtml")); + unitOfWork.Commit(); + } + + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs index f51e50b57a..75c92c7971 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs @@ -1,140 +1,140 @@ -using System.Linq; -using NUnit.Framework; -using Umbraco.Core.Models; -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.Caching; -using Umbraco.Core.Persistence.Repositories; -using Umbraco.Core.Persistence.UnitOfWork; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Entities; - -namespace Umbraco.Tests.Persistence.Repositories -{ - [TestFixture] - public class MemberTypeRepositoryTest : BaseDatabaseFactoryTest - { - [SetUp] - public override void Initialize() - { - base.Initialize(); - } - - [TearDown] - public override void TearDown() - { - base.TearDown(); - } - - private MemberTypeRepository CreateRepository(IDatabaseUnitOfWork unitOfWork) - { - return new MemberTypeRepository(unitOfWork, NullCacheProvider.Current); - } - - [Test] - public void Can_Instantiate_Repository_From_Resolver() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - - // Act - var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); - - // Assert - Assert.That(repository, Is.Not.Null); - } - - [Test] - public void MemberRepository_Can_Persist_Member_Type() - { - IMemberType sut; - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = CreateRepository(unitOfWork)) - { - var memberType = MockedContentTypes.CreateSimpleMemberType(); - repository.AddOrUpdate(memberType); - unitOfWork.Commit(); - - sut = repository.Get(memberType.Id); - - Assert.That(sut, Is.Not.Null); - Assert.That(sut.PropertyGroups.Count(), Is.EqualTo(1)); - Assert.That(sut.PropertyTypes.Count(), Is.EqualTo(12)); - - Assert.That(sut.PropertyGroups.Any(x => x.HasIdentity == false || x.Id == 0), Is.False); - Assert.That(sut.PropertyTypes.Any(x => x.HasIdentity == false || x.Id == 0), Is.False); - } - } - - [Test] - public void MemberRepository_Can_Get_All_Member_Types() - { - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = CreateRepository(unitOfWork)) - { - var memberType1 = MockedContentTypes.CreateSimpleMemberType(); - repository.AddOrUpdate(memberType1); - unitOfWork.Commit(); - - var memberType2 = MockedContentTypes.CreateSimpleMemberType(); - memberType2.Name = "AnotherType"; - memberType2.Alias = "anotherType"; - repository.AddOrUpdate(memberType2); - unitOfWork.Commit(); - - var result = repository.GetAll(); - - //there are 3 because of the Member type created for init data - Assert.AreEqual(3, result.Count()); - } - } - - [Test] - public void MemberRepository_Can_Get_All_Member_When_No_Properties() - { - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = CreateRepository(unitOfWork)) - { - var memberType1 = MockedContentTypes.CreateSimpleMemberType(); - memberType1.PropertyTypeCollection.Clear(); - repository.AddOrUpdate(memberType1); - unitOfWork.Commit(); - - var memberType2 = MockedContentTypes.CreateSimpleMemberType(); - memberType2.PropertyTypeCollection.Clear(); - memberType2.Name = "AnotherType"; - memberType2.Alias = "anotherType"; - repository.AddOrUpdate(memberType2); - unitOfWork.Commit(); - - var result = repository.GetAll(); - - //there are 3 because of the Member type created for init data - Assert.AreEqual(3, result.Count()); - } - } - - [Test, NUnit.Framework.Ignore] - public void MemberTypeRepository_Can_Get_MemberType_By_Id() - { - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = CreateRepository(unitOfWork)) - { - - var memberType = repository.Get(1340); - - Assert.That(memberType, Is.Not.Null); - Assert.That(memberType.PropertyTypes.Count(), Is.EqualTo(13)); - Assert.That(memberType.PropertyGroups.Any(), Is.False); - - repository.AddOrUpdate(memberType); - unitOfWork.Commit(); - Assert.That(memberType.PropertyTypes.Any(x => x.HasIdentity == false), Is.False); - } - } - } +using System.Linq; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Caching; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Entities; + +namespace Umbraco.Tests.Persistence.Repositories +{ + [TestFixture] + public class MemberTypeRepositoryTest : BaseDatabaseFactoryTest + { + [SetUp] + public override void Initialize() + { + base.Initialize(); + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); + } + + private MemberTypeRepository CreateRepository(IDatabaseUnitOfWork unitOfWork) + { + return new MemberTypeRepository(unitOfWork, NullCacheProvider.Current); + } + + [Test] + public void Can_Instantiate_Repository_From_Resolver() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + + // Act + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + + // Assert + Assert.That(repository, Is.Not.Null); + } + + [Test] + public void MemberRepository_Can_Persist_Member_Type() + { + IMemberType sut; + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var memberType = MockedContentTypes.CreateSimpleMemberType(); + repository.AddOrUpdate(memberType); + unitOfWork.Commit(); + + sut = repository.Get(memberType.Id); + + Assert.That(sut, Is.Not.Null); + Assert.That(sut.PropertyGroups.Count(), Is.EqualTo(1)); + Assert.That(sut.PropertyTypes.Count(), Is.EqualTo(12)); + + Assert.That(sut.PropertyGroups.Any(x => x.HasIdentity == false || x.Id == 0), Is.False); + Assert.That(sut.PropertyTypes.Any(x => x.HasIdentity == false || x.Id == 0), Is.False); + } + } + + [Test] + public void MemberRepository_Can_Get_All_Member_Types() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var memberType1 = MockedContentTypes.CreateSimpleMemberType(); + repository.AddOrUpdate(memberType1); + unitOfWork.Commit(); + + var memberType2 = MockedContentTypes.CreateSimpleMemberType(); + memberType2.Name = "AnotherType"; + memberType2.Alias = "anotherType"; + repository.AddOrUpdate(memberType2); + unitOfWork.Commit(); + + var result = repository.GetAll(); + + //there are 3 because of the Member type created for init data + Assert.AreEqual(3, result.Count()); + } + } + + [Test] + public void MemberRepository_Can_Get_All_Member_When_No_Properties() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var memberType1 = MockedContentTypes.CreateSimpleMemberType(); + memberType1.PropertyTypeCollection.Clear(); + repository.AddOrUpdate(memberType1); + unitOfWork.Commit(); + + var memberType2 = MockedContentTypes.CreateSimpleMemberType(); + memberType2.PropertyTypeCollection.Clear(); + memberType2.Name = "AnotherType"; + memberType2.Alias = "anotherType"; + repository.AddOrUpdate(memberType2); + unitOfWork.Commit(); + + var result = repository.GetAll(); + + //there are 3 because of the Member type created for init data + Assert.AreEqual(3, result.Count()); + } + } + + [Test, NUnit.Framework.Ignore] + public void MemberTypeRepository_Can_Get_MemberType_By_Id() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + + var memberType = repository.Get(1340); + + Assert.That(memberType, Is.Not.Null); + Assert.That(memberType.PropertyTypes.Count(), Is.EqualTo(13)); + Assert.That(memberType.PropertyGroups.Any(), Is.False); + + repository.AddOrUpdate(memberType); + unitOfWork.Commit(); + Assert.That(memberType.PropertyTypes.Any(x => x.HasIdentity == false), Is.False); + } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs index b5f934706a..f38b279553 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs @@ -1,721 +1,721 @@ -using System; -using System.Linq; -using NUnit.Framework; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Rdbms; -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.Caching; -using Umbraco.Core.Persistence.Repositories; -using Umbraco.Core.Persistence.UnitOfWork; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Entities; - -namespace Umbraco.Tests.Persistence.Repositories -{ - [TestFixture] - public class TagRepositoryTest : BaseDatabaseFactoryTest - { - [SetUp] - public override void Initialize() - { - base.Initialize(); - } - - [TearDown] - public override void TearDown() - { - base.TearDown(); - } - - private TagsRepository CreateRepository(IDatabaseUnitOfWork unitOfWork) - { - var tagRepository = new TagsRepository(unitOfWork, NullCacheProvider.Current); - return tagRepository; - } - - [Test] - public void Can_Instantiate_Repository_From_Resolver() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - - // Act - var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); - - // Assert - Assert.That(repository, Is.Not.Null); - } - - [Test] - public void Can_Perform_Add_On_Repository() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = CreateRepository(unitOfWork)) - { - var tag = new Tag() - { - Group = "Test", - Text = "Test" - }; - - // Act - repository.AddOrUpdate(tag); - unitOfWork.Commit(); - - // Assert - Assert.That(tag.HasIdentity, Is.True); - } - } - - [Test] - public void Can_Perform_Multiple_Adds_On_Repository() - { - // Arrange - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = CreateRepository(unitOfWork)) - { - var tag = new Tag() - { - Group = "Test", - Text = "Test" - }; - - // Act - repository.AddOrUpdate(tag); - unitOfWork.Commit(); - - var tag2 = new Tag() - { - Group = "Test", - Text = "Test2" - }; - repository.AddOrUpdate(tag2); - unitOfWork.Commit(); - - // Assert - Assert.That(tag.HasIdentity, Is.True); - Assert.That(tag2.HasIdentity, Is.True); - Assert.AreNotEqual(tag.Id, tag2.Id); - } - - } - - [Test] - public void Cannot_Assign_Tags_To_Non_Existing_Property() - { - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - using (var repository = CreateRepository(unitOfWork)) - { - Assert.Throws(() => repository.AssignTagsToProperty(1234, "hello", Enumerable.Empty(), true)); - } - - } - - [Test] - public void Can_Create_Tag_Relations() - { - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - ContentTypeRepository contentTypeRepository; - using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) - { - //create data to relate to - var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); - contentTypeRepository.AddOrUpdate(contentType); - unitOfWork.Commit(); - var content = MockedContent.CreateSimpleContent(contentType); - contentRepository.AddOrUpdate(content); - unitOfWork.Commit(); - - using (var repository = CreateRepository(unitOfWork)) - { - repository.AssignTagsToProperty( - content.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test"}, - }, false); - - Assert.AreEqual(2, repository.GetTagsForEntity(content.Id).Count()); - } - } - } - - [Test] - public void Can_Append_Tag_Relations() - { - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - ContentTypeRepository contentTypeRepository; - using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) - { - //create data to relate to - var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); - contentTypeRepository.AddOrUpdate(contentType); - unitOfWork.Commit(); - var content = MockedContent.CreateSimpleContent(contentType); - contentRepository.AddOrUpdate(content); - unitOfWork.Commit(); - - using (var repository = CreateRepository(unitOfWork)) - { - repository.AssignTagsToProperty( - content.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test"}, - }, false); - - repository.AssignTagsToProperty( - content.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag3", Group = "test"}, - new Tag {Text = "tag4", Group = "test"}, - }, false); - - Assert.AreEqual(4, repository.GetTagsForEntity(content.Id).Count()); - } - } - } - - [Test] - public void Can_Replace_Tag_Relations() - { - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - ContentTypeRepository contentTypeRepository; - using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) - { - //create data to relate to - var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); - contentTypeRepository.AddOrUpdate(contentType); - unitOfWork.Commit(); - var content = MockedContent.CreateSimpleContent(contentType); - contentRepository.AddOrUpdate(content); - unitOfWork.Commit(); - - using (var repository = CreateRepository(unitOfWork)) - { - repository.AssignTagsToProperty( - content.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test"}, - }, false); - - repository.AssignTagsToProperty( - content.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag3", Group = "test"}, - new Tag {Text = "tag4", Group = "test"}, - }, true); - - var result = repository.GetTagsForEntity(content.Id); - Assert.AreEqual(2, result.Count()); - Assert.AreEqual("tag3", result.ElementAt(0).Text); - Assert.AreEqual("tag4", result.ElementAt(1).Text); - } - } - } - - [Test] - public void Can_Merge_Tag_Relations() - { - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - ContentTypeRepository contentTypeRepository; - using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) - { - //create data to relate to - var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); - contentTypeRepository.AddOrUpdate(contentType); - unitOfWork.Commit(); - var content = MockedContent.CreateSimpleContent(contentType); - contentRepository.AddOrUpdate(content); - unitOfWork.Commit(); - - using (var repository = CreateRepository(unitOfWork)) - { - repository.AssignTagsToProperty( - content.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test"}, - }, false); - - repository.AssignTagsToProperty( - content.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag2", Group = "test"}, - new Tag {Text = "tag3", Group = "test"}, - }, false); - - var result = repository.GetTagsForEntity(content.Id); - Assert.AreEqual(3, result.Count()); - } - } - } - - [Test] - public void Can_Clear_Tag_Relations() - { - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - ContentTypeRepository contentTypeRepository; - using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) - { - //create data to relate to - var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); - contentTypeRepository.AddOrUpdate(contentType); - unitOfWork.Commit(); - var content = MockedContent.CreateSimpleContent(contentType); - contentRepository.AddOrUpdate(content); - unitOfWork.Commit(); - - using (var repository = CreateRepository(unitOfWork)) - { - repository.AssignTagsToProperty( - content.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test"}, - }, false); - - repository.AssignTagsToProperty( - content.Id, - contentType.PropertyTypes.First().Alias, - Enumerable.Empty(), true); - - var result = repository.GetTagsForEntity(content.Id); - Assert.AreEqual(0, result.Count()); - } - } - } - - [Test] - public void Can_Remove_Specific_Tags_From_Property() - { - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - ContentTypeRepository contentTypeRepository; - using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) - { - //create data to relate to - var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); - contentTypeRepository.AddOrUpdate(contentType); - unitOfWork.Commit(); - var content = MockedContent.CreateSimpleContent(contentType); - contentRepository.AddOrUpdate(content); - unitOfWork.Commit(); - - using (var repository = CreateRepository(unitOfWork)) - { - repository.AssignTagsToProperty( - content.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test"}, - new Tag {Text = "tag3", Group = "test"}, - new Tag {Text = "tag4", Group = "test"} - }, false); - - repository.RemoveTagsFromProperty( - content.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag2", Group = "test"}, - new Tag {Text = "tag3", Group = "test"} - }); - - var result = repository.GetTagsForEntity(content.Id); - Assert.AreEqual(2, result.Count()); - Assert.AreEqual("tag1", result.ElementAt(0).Text); - Assert.AreEqual("tag4", result.ElementAt(1).Text); - } - } - } - - [Test] - public void Can_Get_Tags_For_Content() - { - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - ContentTypeRepository contentTypeRepository; - using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) - { - //create data to relate to - var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); - contentTypeRepository.AddOrUpdate(contentType); - unitOfWork.Commit(); - var content1 = MockedContent.CreateSimpleContent(contentType); - contentRepository.AddOrUpdate(content1); - var content2 = MockedContent.CreateSimpleContent(contentType); - contentRepository.AddOrUpdate(content2); - unitOfWork.Commit(); - - using (var repository = CreateRepository(unitOfWork)) - { - repository.AssignTagsToProperty( - content1.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test"}, - new Tag {Text = "tag3", Group = "test"}, - new Tag {Text = "tag4", Group = "test"} - }, false); - - repository.AssignTagsToProperty( - content2.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test"} - }, false); - - var result = repository.GetTagsForEntity(content2.Id); - Assert.AreEqual(2, result.Count()); - } - } - } - - [Test] - public void Can_Get_Tags_For_Content_For_Group() - { - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - ContentTypeRepository contentTypeRepository; - using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) - { - //create data to relate to - var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); - contentTypeRepository.AddOrUpdate(contentType); - unitOfWork.Commit(); - var content1 = MockedContent.CreateSimpleContent(contentType); - contentRepository.AddOrUpdate(content1); - var content2 = MockedContent.CreateSimpleContent(contentType); - contentRepository.AddOrUpdate(content2); - unitOfWork.Commit(); - - using (var repository = CreateRepository(unitOfWork)) - { - repository.AssignTagsToProperty( - content1.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test1"}, - new Tag {Text = "tag3", Group = "test"}, - new Tag {Text = "tag4", Group = "test1"} - }, false); - - repository.AssignTagsToProperty( - content2.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test"} - }, false); - - var result = repository.GetTagsForEntity(content1.Id, "test1"); - Assert.AreEqual(2, result.Count()); - } - } - } - - [Test] - public void Can_Get_Tags_For_Property() - { - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - ContentTypeRepository contentTypeRepository; - using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) - { - //create data to relate to - var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); - contentTypeRepository.AddOrUpdate(contentType); - unitOfWork.Commit(); - var content1 = MockedContent.CreateSimpleContent(contentType); - contentRepository.AddOrUpdate(content1); - unitOfWork.Commit(); - - using (var repository = CreateRepository(unitOfWork)) - { - repository.AssignTagsToProperty( - content1.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test"}, - new Tag {Text = "tag3", Group = "test"}, - new Tag {Text = "tag4", Group = "test"} - }, false); - - repository.AssignTagsToProperty( - content1.Id, - contentType.PropertyTypes.Last().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test"} - }, false); - - var result1 = repository.GetTagsForProperty(content1.Id, contentType.PropertyTypes.First().Alias).ToArray(); - var result2 = repository.GetTagsForProperty(content1.Id, contentType.PropertyTypes.Last().Alias).ToArray(); - Assert.AreEqual(4, result1.Count()); - Assert.AreEqual(2, result2.Count()); - } - } - - } - - [Test] - public void Can_Get_Tags_For_Property_For_Group() - { - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - ContentTypeRepository contentTypeRepository; - using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) - { - //create data to relate to - var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); - contentTypeRepository.AddOrUpdate(contentType); - unitOfWork.Commit(); - var content1 = MockedContent.CreateSimpleContent(contentType); - contentRepository.AddOrUpdate(content1); - unitOfWork.Commit(); - - using (var repository = CreateRepository(unitOfWork)) - { - repository.AssignTagsToProperty( - content1.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test1"}, - new Tag {Text = "tag3", Group = "test"}, - new Tag {Text = "tag4", Group = "test1"} - }, false); - - repository.AssignTagsToProperty( - content1.Id, - contentType.PropertyTypes.Last().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test1"} - }, false); - - var result1 = repository.GetTagsForProperty(content1.Id, contentType.PropertyTypes.First().Alias, "test1").ToArray(); - var result2 = repository.GetTagsForProperty(content1.Id, contentType.PropertyTypes.Last().Alias, "test1").ToArray(); - - Assert.AreEqual(2, result1.Count()); - Assert.AreEqual(1, result2.Count()); - - } - } - - } - - [Test] - public void Can_Get_Tags_For_Entity_Type() - { - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - MediaTypeRepository mediaTypeRepository; - ContentTypeRepository contentTypeRepository; - using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) - using (var mediaRepository = CreateMediaRepository(unitOfWork, out mediaTypeRepository)) - { - //create data to relate to - var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); - contentTypeRepository.AddOrUpdate(contentType); - unitOfWork.Commit(); - var content1 = MockedContent.CreateSimpleContent(contentType); - contentRepository.AddOrUpdate(content1); - unitOfWork.Commit(); - var mediaType = MockedContentTypes.CreateImageMediaType(); - mediaTypeRepository.AddOrUpdate(mediaType); - unitOfWork.Commit(); - var media1 = MockedMedia.CreateMediaImage(mediaType, -1); - mediaRepository.AddOrUpdate(media1); - unitOfWork.Commit(); - - using (var repository = CreateRepository(unitOfWork)) - { - repository.AssignTagsToProperty( - content1.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test1"}, - new Tag {Text = "tag3", Group = "test"} - }, false); - - repository.AssignTagsToProperty( - media1.Id, - mediaType.PropertyTypes.Last().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test1"} - }, false); - - var result1 = repository.GetTagsForEntityType(TaggableObjectTypes.Content).ToArray(); - var result2 = repository.GetTagsForEntityType(TaggableObjectTypes.Media).ToArray(); - - Assert.AreEqual(3, result1.Count()); - Assert.AreEqual(2, result2.Count()); - - } - } - - } - - [Test] - public void Can_Get_Tags_For_Entity_Type_For_Group() - { - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - MediaTypeRepository mediaTypeRepository; - ContentTypeRepository contentTypeRepository; - using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) - using (var mediaRepository = CreateMediaRepository(unitOfWork, out mediaTypeRepository)) - { - //create data to relate to - var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); - contentTypeRepository.AddOrUpdate(contentType); - unitOfWork.Commit(); - var content1 = MockedContent.CreateSimpleContent(contentType); - contentRepository.AddOrUpdate(content1); - unitOfWork.Commit(); - var mediaType = MockedContentTypes.CreateImageMediaType(); - mediaTypeRepository.AddOrUpdate(mediaType); - unitOfWork.Commit(); - var media1 = MockedMedia.CreateMediaImage(mediaType, -1); - mediaRepository.AddOrUpdate(media1); - unitOfWork.Commit(); - - using (var repository = CreateRepository(unitOfWork)) - { - repository.AssignTagsToProperty( - content1.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test1"}, - new Tag {Text = "tag3", Group = "test"}, - new Tag {Text = "tag4", Group = "test1"} - }, false); - - repository.AssignTagsToProperty( - media1.Id, - mediaType.PropertyTypes.Last().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test1"} - }, false); - - var result1 = repository.GetTagsForEntityType(TaggableObjectTypes.Content, "test1").ToArray(); - var result2 = repository.GetTagsForEntityType(TaggableObjectTypes.Media, "test1").ToArray(); - - Assert.AreEqual(2, result1.Count()); - Assert.AreEqual(1, result2.Count()); - - } - } - - } - - [Test] - public void Cascade_Deletes_Tag_Relations() - { - var provider = new PetaPocoUnitOfWorkProvider(); - var unitOfWork = provider.GetUnitOfWork(); - ContentTypeRepository contentTypeRepository; - using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) - { - //create data to relate to - var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); - contentTypeRepository.AddOrUpdate(contentType); - unitOfWork.Commit(); - var content1 = MockedContent.CreateSimpleContent(contentType); - contentRepository.AddOrUpdate(content1); - unitOfWork.Commit(); - - using (var repository = CreateRepository(unitOfWork)) - { - repository.AssignTagsToProperty( - content1.Id, - contentType.PropertyTypes.First().Alias, - new[] - { - new Tag {Text = "tag1", Group = "test"}, - new Tag {Text = "tag2", Group = "test"}, - new Tag {Text = "tag3", Group = "test"}, - new Tag {Text = "tag4", Group = "test"} - }, false); - - contentRepository.Delete(content1); - - unitOfWork.Commit(); - - Assert.AreEqual(0, DatabaseContext.Database.ExecuteScalar( - "SELECT COUNT(*) FROM cmsTagRelationship WHERE nodeId=@nodeId AND propertyTypeId=@propTypeId", - new { nodeId = content1.Id, propTypeId = contentType.PropertyTypes.First().Id })); - } - } - - } - - private ContentRepository CreateContentRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository) - { - var templateRepository = new TemplateRepository(unitOfWork, NullCacheProvider.Current); - var tagRepository = new TagsRepository(unitOfWork, NullCacheProvider.Current); - contentTypeRepository = new ContentTypeRepository(unitOfWork, NullCacheProvider.Current, templateRepository); - var repository = new ContentRepository(unitOfWork, NullCacheProvider.Current, contentTypeRepository, templateRepository, tagRepository); - return repository; - } - - private MediaRepository CreateMediaRepository(IDatabaseUnitOfWork unitOfWork, out MediaTypeRepository mediaTypeRepository) - { - var tagRepository = new TagsRepository(unitOfWork, NullCacheProvider.Current); - mediaTypeRepository = new MediaTypeRepository(unitOfWork, NullCacheProvider.Current); - var repository = new MediaRepository(unitOfWork, NullCacheProvider.Current, mediaTypeRepository, tagRepository); - return repository; - } - } +using System; +using System.Linq; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Caching; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Entities; + +namespace Umbraco.Tests.Persistence.Repositories +{ + [TestFixture] + public class TagRepositoryTest : BaseDatabaseFactoryTest + { + [SetUp] + public override void Initialize() + { + base.Initialize(); + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); + } + + private TagsRepository CreateRepository(IDatabaseUnitOfWork unitOfWork) + { + var tagRepository = new TagsRepository(unitOfWork, NullCacheProvider.Current); + return tagRepository; + } + + [Test] + public void Can_Instantiate_Repository_From_Resolver() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + + // Act + var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); + + // Assert + Assert.That(repository, Is.Not.Null); + } + + [Test] + public void Can_Perform_Add_On_Repository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var tag = new Tag() + { + Group = "Test", + Text = "Test" + }; + + // Act + repository.AddOrUpdate(tag); + unitOfWork.Commit(); + + // Assert + Assert.That(tag.HasIdentity, Is.True); + } + } + + [Test] + public void Can_Perform_Multiple_Adds_On_Repository() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var tag = new Tag() + { + Group = "Test", + Text = "Test" + }; + + // Act + repository.AddOrUpdate(tag); + unitOfWork.Commit(); + + var tag2 = new Tag() + { + Group = "Test", + Text = "Test2" + }; + repository.AddOrUpdate(tag2); + unitOfWork.Commit(); + + // Assert + Assert.That(tag.HasIdentity, Is.True); + Assert.That(tag2.HasIdentity, Is.True); + Assert.AreNotEqual(tag.Id, tag2.Id); + } + + } + + [Test] + public void Cannot_Assign_Tags_To_Non_Existing_Property() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + Assert.Throws(() => repository.AssignTagsToProperty(1234, "hello", Enumerable.Empty(), true)); + } + + } + + [Test] + public void Can_Create_Tag_Relations() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + ContentTypeRepository contentTypeRepository; + using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) + { + //create data to relate to + var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + contentTypeRepository.AddOrUpdate(contentType); + unitOfWork.Commit(); + var content = MockedContent.CreateSimpleContent(contentType); + contentRepository.AddOrUpdate(content); + unitOfWork.Commit(); + + using (var repository = CreateRepository(unitOfWork)) + { + repository.AssignTagsToProperty( + content.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test"}, + }, false); + + Assert.AreEqual(2, repository.GetTagsForEntity(content.Id).Count()); + } + } + } + + [Test] + public void Can_Append_Tag_Relations() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + ContentTypeRepository contentTypeRepository; + using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) + { + //create data to relate to + var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + contentTypeRepository.AddOrUpdate(contentType); + unitOfWork.Commit(); + var content = MockedContent.CreateSimpleContent(contentType); + contentRepository.AddOrUpdate(content); + unitOfWork.Commit(); + + using (var repository = CreateRepository(unitOfWork)) + { + repository.AssignTagsToProperty( + content.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test"}, + }, false); + + repository.AssignTagsToProperty( + content.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag3", Group = "test"}, + new Tag {Text = "tag4", Group = "test"}, + }, false); + + Assert.AreEqual(4, repository.GetTagsForEntity(content.Id).Count()); + } + } + } + + [Test] + public void Can_Replace_Tag_Relations() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + ContentTypeRepository contentTypeRepository; + using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) + { + //create data to relate to + var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + contentTypeRepository.AddOrUpdate(contentType); + unitOfWork.Commit(); + var content = MockedContent.CreateSimpleContent(contentType); + contentRepository.AddOrUpdate(content); + unitOfWork.Commit(); + + using (var repository = CreateRepository(unitOfWork)) + { + repository.AssignTagsToProperty( + content.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test"}, + }, false); + + repository.AssignTagsToProperty( + content.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag3", Group = "test"}, + new Tag {Text = "tag4", Group = "test"}, + }, true); + + var result = repository.GetTagsForEntity(content.Id); + Assert.AreEqual(2, result.Count()); + Assert.AreEqual("tag3", result.ElementAt(0).Text); + Assert.AreEqual("tag4", result.ElementAt(1).Text); + } + } + } + + [Test] + public void Can_Merge_Tag_Relations() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + ContentTypeRepository contentTypeRepository; + using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) + { + //create data to relate to + var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + contentTypeRepository.AddOrUpdate(contentType); + unitOfWork.Commit(); + var content = MockedContent.CreateSimpleContent(contentType); + contentRepository.AddOrUpdate(content); + unitOfWork.Commit(); + + using (var repository = CreateRepository(unitOfWork)) + { + repository.AssignTagsToProperty( + content.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test"}, + }, false); + + repository.AssignTagsToProperty( + content.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag2", Group = "test"}, + new Tag {Text = "tag3", Group = "test"}, + }, false); + + var result = repository.GetTagsForEntity(content.Id); + Assert.AreEqual(3, result.Count()); + } + } + } + + [Test] + public void Can_Clear_Tag_Relations() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + ContentTypeRepository contentTypeRepository; + using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) + { + //create data to relate to + var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + contentTypeRepository.AddOrUpdate(contentType); + unitOfWork.Commit(); + var content = MockedContent.CreateSimpleContent(contentType); + contentRepository.AddOrUpdate(content); + unitOfWork.Commit(); + + using (var repository = CreateRepository(unitOfWork)) + { + repository.AssignTagsToProperty( + content.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test"}, + }, false); + + repository.AssignTagsToProperty( + content.Id, + contentType.PropertyTypes.First().Alias, + Enumerable.Empty(), true); + + var result = repository.GetTagsForEntity(content.Id); + Assert.AreEqual(0, result.Count()); + } + } + } + + [Test] + public void Can_Remove_Specific_Tags_From_Property() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + ContentTypeRepository contentTypeRepository; + using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) + { + //create data to relate to + var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + contentTypeRepository.AddOrUpdate(contentType); + unitOfWork.Commit(); + var content = MockedContent.CreateSimpleContent(contentType); + contentRepository.AddOrUpdate(content); + unitOfWork.Commit(); + + using (var repository = CreateRepository(unitOfWork)) + { + repository.AssignTagsToProperty( + content.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test"}, + new Tag {Text = "tag3", Group = "test"}, + new Tag {Text = "tag4", Group = "test"} + }, false); + + repository.RemoveTagsFromProperty( + content.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag2", Group = "test"}, + new Tag {Text = "tag3", Group = "test"} + }); + + var result = repository.GetTagsForEntity(content.Id); + Assert.AreEqual(2, result.Count()); + Assert.AreEqual("tag1", result.ElementAt(0).Text); + Assert.AreEqual("tag4", result.ElementAt(1).Text); + } + } + } + + [Test] + public void Can_Get_Tags_For_Content() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + ContentTypeRepository contentTypeRepository; + using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) + { + //create data to relate to + var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + contentTypeRepository.AddOrUpdate(contentType); + unitOfWork.Commit(); + var content1 = MockedContent.CreateSimpleContent(contentType); + contentRepository.AddOrUpdate(content1); + var content2 = MockedContent.CreateSimpleContent(contentType); + contentRepository.AddOrUpdate(content2); + unitOfWork.Commit(); + + using (var repository = CreateRepository(unitOfWork)) + { + repository.AssignTagsToProperty( + content1.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test"}, + new Tag {Text = "tag3", Group = "test"}, + new Tag {Text = "tag4", Group = "test"} + }, false); + + repository.AssignTagsToProperty( + content2.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test"} + }, false); + + var result = repository.GetTagsForEntity(content2.Id); + Assert.AreEqual(2, result.Count()); + } + } + } + + [Test] + public void Can_Get_Tags_For_Content_For_Group() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + ContentTypeRepository contentTypeRepository; + using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) + { + //create data to relate to + var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + contentTypeRepository.AddOrUpdate(contentType); + unitOfWork.Commit(); + var content1 = MockedContent.CreateSimpleContent(contentType); + contentRepository.AddOrUpdate(content1); + var content2 = MockedContent.CreateSimpleContent(contentType); + contentRepository.AddOrUpdate(content2); + unitOfWork.Commit(); + + using (var repository = CreateRepository(unitOfWork)) + { + repository.AssignTagsToProperty( + content1.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test1"}, + new Tag {Text = "tag3", Group = "test"}, + new Tag {Text = "tag4", Group = "test1"} + }, false); + + repository.AssignTagsToProperty( + content2.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test"} + }, false); + + var result = repository.GetTagsForEntity(content1.Id, "test1"); + Assert.AreEqual(2, result.Count()); + } + } + } + + [Test] + public void Can_Get_Tags_For_Property() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + ContentTypeRepository contentTypeRepository; + using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) + { + //create data to relate to + var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + contentTypeRepository.AddOrUpdate(contentType); + unitOfWork.Commit(); + var content1 = MockedContent.CreateSimpleContent(contentType); + contentRepository.AddOrUpdate(content1); + unitOfWork.Commit(); + + using (var repository = CreateRepository(unitOfWork)) + { + repository.AssignTagsToProperty( + content1.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test"}, + new Tag {Text = "tag3", Group = "test"}, + new Tag {Text = "tag4", Group = "test"} + }, false); + + repository.AssignTagsToProperty( + content1.Id, + contentType.PropertyTypes.Last().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test"} + }, false); + + var result1 = repository.GetTagsForProperty(content1.Id, contentType.PropertyTypes.First().Alias).ToArray(); + var result2 = repository.GetTagsForProperty(content1.Id, contentType.PropertyTypes.Last().Alias).ToArray(); + Assert.AreEqual(4, result1.Count()); + Assert.AreEqual(2, result2.Count()); + } + } + + } + + [Test] + public void Can_Get_Tags_For_Property_For_Group() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + ContentTypeRepository contentTypeRepository; + using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) + { + //create data to relate to + var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + contentTypeRepository.AddOrUpdate(contentType); + unitOfWork.Commit(); + var content1 = MockedContent.CreateSimpleContent(contentType); + contentRepository.AddOrUpdate(content1); + unitOfWork.Commit(); + + using (var repository = CreateRepository(unitOfWork)) + { + repository.AssignTagsToProperty( + content1.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test1"}, + new Tag {Text = "tag3", Group = "test"}, + new Tag {Text = "tag4", Group = "test1"} + }, false); + + repository.AssignTagsToProperty( + content1.Id, + contentType.PropertyTypes.Last().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test1"} + }, false); + + var result1 = repository.GetTagsForProperty(content1.Id, contentType.PropertyTypes.First().Alias, "test1").ToArray(); + var result2 = repository.GetTagsForProperty(content1.Id, contentType.PropertyTypes.Last().Alias, "test1").ToArray(); + + Assert.AreEqual(2, result1.Count()); + Assert.AreEqual(1, result2.Count()); + + } + } + + } + + [Test] + public void Can_Get_Tags_For_Entity_Type() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + MediaTypeRepository mediaTypeRepository; + ContentTypeRepository contentTypeRepository; + using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) + using (var mediaRepository = CreateMediaRepository(unitOfWork, out mediaTypeRepository)) + { + //create data to relate to + var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + contentTypeRepository.AddOrUpdate(contentType); + unitOfWork.Commit(); + var content1 = MockedContent.CreateSimpleContent(contentType); + contentRepository.AddOrUpdate(content1); + unitOfWork.Commit(); + var mediaType = MockedContentTypes.CreateImageMediaType(); + mediaTypeRepository.AddOrUpdate(mediaType); + unitOfWork.Commit(); + var media1 = MockedMedia.CreateMediaImage(mediaType, -1); + mediaRepository.AddOrUpdate(media1); + unitOfWork.Commit(); + + using (var repository = CreateRepository(unitOfWork)) + { + repository.AssignTagsToProperty( + content1.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test1"}, + new Tag {Text = "tag3", Group = "test"} + }, false); + + repository.AssignTagsToProperty( + media1.Id, + mediaType.PropertyTypes.Last().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test1"} + }, false); + + var result1 = repository.GetTagsForEntityType(TaggableObjectTypes.Content).ToArray(); + var result2 = repository.GetTagsForEntityType(TaggableObjectTypes.Media).ToArray(); + + Assert.AreEqual(3, result1.Count()); + Assert.AreEqual(2, result2.Count()); + + } + } + + } + + [Test] + public void Can_Get_Tags_For_Entity_Type_For_Group() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + MediaTypeRepository mediaTypeRepository; + ContentTypeRepository contentTypeRepository; + using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) + using (var mediaRepository = CreateMediaRepository(unitOfWork, out mediaTypeRepository)) + { + //create data to relate to + var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + contentTypeRepository.AddOrUpdate(contentType); + unitOfWork.Commit(); + var content1 = MockedContent.CreateSimpleContent(contentType); + contentRepository.AddOrUpdate(content1); + unitOfWork.Commit(); + var mediaType = MockedContentTypes.CreateImageMediaType(); + mediaTypeRepository.AddOrUpdate(mediaType); + unitOfWork.Commit(); + var media1 = MockedMedia.CreateMediaImage(mediaType, -1); + mediaRepository.AddOrUpdate(media1); + unitOfWork.Commit(); + + using (var repository = CreateRepository(unitOfWork)) + { + repository.AssignTagsToProperty( + content1.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test1"}, + new Tag {Text = "tag3", Group = "test"}, + new Tag {Text = "tag4", Group = "test1"} + }, false); + + repository.AssignTagsToProperty( + media1.Id, + mediaType.PropertyTypes.Last().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test1"} + }, false); + + var result1 = repository.GetTagsForEntityType(TaggableObjectTypes.Content, "test1").ToArray(); + var result2 = repository.GetTagsForEntityType(TaggableObjectTypes.Media, "test1").ToArray(); + + Assert.AreEqual(2, result1.Count()); + Assert.AreEqual(1, result2.Count()); + + } + } + + } + + [Test] + public void Cascade_Deletes_Tag_Relations() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + ContentTypeRepository contentTypeRepository; + using (var contentRepository = CreateContentRepository(unitOfWork, out contentTypeRepository)) + { + //create data to relate to + var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + contentTypeRepository.AddOrUpdate(contentType); + unitOfWork.Commit(); + var content1 = MockedContent.CreateSimpleContent(contentType); + contentRepository.AddOrUpdate(content1); + unitOfWork.Commit(); + + using (var repository = CreateRepository(unitOfWork)) + { + repository.AssignTagsToProperty( + content1.Id, + contentType.PropertyTypes.First().Alias, + new[] + { + new Tag {Text = "tag1", Group = "test"}, + new Tag {Text = "tag2", Group = "test"}, + new Tag {Text = "tag3", Group = "test"}, + new Tag {Text = "tag4", Group = "test"} + }, false); + + contentRepository.Delete(content1); + + unitOfWork.Commit(); + + Assert.AreEqual(0, DatabaseContext.Database.ExecuteScalar( + "SELECT COUNT(*) FROM cmsTagRelationship WHERE nodeId=@nodeId AND propertyTypeId=@propTypeId", + new { nodeId = content1.Id, propTypeId = contentType.PropertyTypes.First().Id })); + } + } + + } + + private ContentRepository CreateContentRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository) + { + var templateRepository = new TemplateRepository(unitOfWork, NullCacheProvider.Current); + var tagRepository = new TagsRepository(unitOfWork, NullCacheProvider.Current); + contentTypeRepository = new ContentTypeRepository(unitOfWork, NullCacheProvider.Current, templateRepository); + var repository = new ContentRepository(unitOfWork, NullCacheProvider.Current, contentTypeRepository, templateRepository, tagRepository); + return repository; + } + + private MediaRepository CreateMediaRepository(IDatabaseUnitOfWork unitOfWork, out MediaTypeRepository mediaTypeRepository) + { + var tagRepository = new TagsRepository(unitOfWork, NullCacheProvider.Current); + mediaTypeRepository = new MediaTypeRepository(unitOfWork, NullCacheProvider.Current); + var repository = new MediaRepository(unitOfWork, NullCacheProvider.Current, mediaTypeRepository, tagRepository); + return repository; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/PropertyEditors/LegacyPropertyEditorIdToAliasConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/LegacyPropertyEditorIdToAliasConverterTests.cs index 63b1c6bcc5..432973d13b 100644 --- a/src/Umbraco.Tests/PropertyEditors/LegacyPropertyEditorIdToAliasConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/LegacyPropertyEditorIdToAliasConverterTests.cs @@ -1,70 +1,70 @@ -using System; -using NUnit.Framework; -using Umbraco.Core.PropertyEditors; - -namespace Umbraco.Tests.PropertyEditors -{ - [TestFixture] - public class LegacyPropertyEditorIdToAliasConverterTests - { - [TearDown] - public void Reset() - { - LegacyPropertyEditorIdToAliasConverter.Reset(); - } - - [Test] - public void Doesnt_Create_Duplicates_Without_Exception() - { - var id = Guid.NewGuid(); - LegacyPropertyEditorIdToAliasConverter.CreateMap(id, "test"); - LegacyPropertyEditorIdToAliasConverter.CreateMap(id, "test"); - Assert.AreEqual(1, LegacyPropertyEditorIdToAliasConverter.Count()); - } - - [Test] - public void Can_Get_Legacy_Id_From_Alias() - { - var id = Guid.NewGuid(); - LegacyPropertyEditorIdToAliasConverter.CreateMap(id, "test"); - - Assert.AreEqual( - id, - LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias( - "test", - LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.ThrowException)); - } - - [Test] - public void Can_Get_Alias_From_Legacy_Id() - { - var id = Guid.NewGuid(); - LegacyPropertyEditorIdToAliasConverter.CreateMap(id, "test"); - - Assert.AreEqual( - "test", - LegacyPropertyEditorIdToAliasConverter.GetAliasFromLegacyId( - id, - true)); - } - - [Test] - public void Can_Generate_Id_From_Missing_Alias() - { - var gen1 = LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias("Donotfindthisone", LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.GenerateId); - var gen2 = LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias("Donotfindthisone", LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.GenerateId); - - Assert.IsNotNull(gen1); - Assert.IsNotNull(gen2); - Assert.AreEqual(gen1, gen2); - } - - [Test] - public void Creates_Map_For_Core_Editors() - { - LegacyPropertyEditorIdToAliasConverter.CreateMappingsForCoreEditors(); - - Assert.AreEqual(37, LegacyPropertyEditorIdToAliasConverter.Count()); - } - } +using System; +using NUnit.Framework; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Tests.PropertyEditors +{ + [TestFixture] + public class LegacyPropertyEditorIdToAliasConverterTests + { + [TearDown] + public void Reset() + { + LegacyPropertyEditorIdToAliasConverter.Reset(); + } + + [Test] + public void Doesnt_Create_Duplicates_Without_Exception() + { + var id = Guid.NewGuid(); + LegacyPropertyEditorIdToAliasConverter.CreateMap(id, "test"); + LegacyPropertyEditorIdToAliasConverter.CreateMap(id, "test"); + Assert.AreEqual(1, LegacyPropertyEditorIdToAliasConverter.Count()); + } + + [Test] + public void Can_Get_Legacy_Id_From_Alias() + { + var id = Guid.NewGuid(); + LegacyPropertyEditorIdToAliasConverter.CreateMap(id, "test"); + + Assert.AreEqual( + id, + LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias( + "test", + LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.ThrowException)); + } + + [Test] + public void Can_Get_Alias_From_Legacy_Id() + { + var id = Guid.NewGuid(); + LegacyPropertyEditorIdToAliasConverter.CreateMap(id, "test"); + + Assert.AreEqual( + "test", + LegacyPropertyEditorIdToAliasConverter.GetAliasFromLegacyId( + id, + true)); + } + + [Test] + public void Can_Generate_Id_From_Missing_Alias() + { + var gen1 = LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias("Donotfindthisone", LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.GenerateId); + var gen2 = LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias("Donotfindthisone", LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.GenerateId); + + Assert.IsNotNull(gen1); + Assert.IsNotNull(gen2); + Assert.AreEqual(gen1, gen2); + } + + [Test] + public void Creates_Map_For_Core_Editors() + { + LegacyPropertyEditorIdToAliasConverter.CreateMappingsForCoreEditors(); + + Assert.AreEqual(37, LegacyPropertyEditorIdToAliasConverter.Count()); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs index 3df4b23a34..6dd6f3f23b 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs @@ -1,110 +1,110 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using NUnit.Framework; -using Umbraco.Core.Models; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web; - -namespace Umbraco.Tests.PublishedContent -{ - [TestFixture] - public class PublishedContentExtensionTests : PublishedContentTestBase - { - private UmbracoContext ctx; - private string xmlContent = ""; - private bool createContentTypes = true; - - protected override DatabaseBehavior DatabaseTestBehavior - { - get { return DatabaseBehavior.NewSchemaPerFixture; } - } - - [SetUp] - public override void Initialize() - { - base.Initialize(); - } - - [TearDown] - public override void TearDown() - { - base.TearDown(); - } - - protected override string GetXmlContent(int templateId) - { - return xmlContent; - } - - [Test] - public void IsDocumentType_NonRecursive_ActualType_ReturnsTrue() - { - InitializeInheritedContentTypes(); - - var publishedContent = ctx.ContentCache.GetById(1100); - Assert.That(publishedContent.IsDocumentType("inherited", false)); - } - - [Test] - public void IsDocumentType_NonRecursive_BaseType_ReturnsFalse() - { - InitializeInheritedContentTypes(); - - var publishedContent = ctx.ContentCache.GetById(1100); - Assert.That(publishedContent.IsDocumentType("base", false), Is.False); - } - - [Test] - public void IsDocumentType_Recursive_ActualType_ReturnsTrue() - { - InitializeInheritedContentTypes(); - - var publishedContent = ctx.ContentCache.GetById(1100); - Assert.That(publishedContent.IsDocumentType("inherited", true)); - } - - [Test] - public void IsDocumentType_Recursive_BaseType_ReturnsTrue() - { - InitializeInheritedContentTypes(); - - var publishedContent = ctx.ContentCache.GetById(1100); - Assert.That(publishedContent.IsDocumentType("base", true)); - } - - [Test] - public void IsDocumentType_Recursive_InvalidBaseType_ReturnsFalse() - { - InitializeInheritedContentTypes(); - - var publishedContent = ctx.ContentCache.GetById(1100); - Assert.That(publishedContent.IsDocumentType("invalidbase", true), Is.False); - } - - private void InitializeInheritedContentTypes() - { - ctx = GetUmbracoContext("/", 1, null, true); - if (createContentTypes) - { - var contentTypeService = ctx.Application.Services.ContentTypeService; - var baseType = new ContentType(-1) {Alias = "base", Name = "Base"}; - var inheritedType = new ContentType(baseType) {Alias = "inherited", Name = "Inherited"}; - contentTypeService.Save(baseType); - contentTypeService.Save(inheritedType); - createContentTypes = false; - } - #region setup xml content - xmlContent = @" - - -]> - - -"; - #endregion - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web; + +namespace Umbraco.Tests.PublishedContent +{ + [TestFixture] + public class PublishedContentExtensionTests : PublishedContentTestBase + { + private UmbracoContext ctx; + private string xmlContent = ""; + private bool createContentTypes = true; + + protected override DatabaseBehavior DatabaseTestBehavior + { + get { return DatabaseBehavior.NewSchemaPerFixture; } + } + + [SetUp] + public override void Initialize() + { + base.Initialize(); + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); + } + + protected override string GetXmlContent(int templateId) + { + return xmlContent; + } + + [Test] + public void IsDocumentType_NonRecursive_ActualType_ReturnsTrue() + { + InitializeInheritedContentTypes(); + + var publishedContent = ctx.ContentCache.GetById(1100); + Assert.That(publishedContent.IsDocumentType("inherited", false)); + } + + [Test] + public void IsDocumentType_NonRecursive_BaseType_ReturnsFalse() + { + InitializeInheritedContentTypes(); + + var publishedContent = ctx.ContentCache.GetById(1100); + Assert.That(publishedContent.IsDocumentType("base", false), Is.False); + } + + [Test] + public void IsDocumentType_Recursive_ActualType_ReturnsTrue() + { + InitializeInheritedContentTypes(); + + var publishedContent = ctx.ContentCache.GetById(1100); + Assert.That(publishedContent.IsDocumentType("inherited", true)); + } + + [Test] + public void IsDocumentType_Recursive_BaseType_ReturnsTrue() + { + InitializeInheritedContentTypes(); + + var publishedContent = ctx.ContentCache.GetById(1100); + Assert.That(publishedContent.IsDocumentType("base", true)); + } + + [Test] + public void IsDocumentType_Recursive_InvalidBaseType_ReturnsFalse() + { + InitializeInheritedContentTypes(); + + var publishedContent = ctx.ContentCache.GetById(1100); + Assert.That(publishedContent.IsDocumentType("invalidbase", true), Is.False); + } + + private void InitializeInheritedContentTypes() + { + ctx = GetUmbracoContext("/", 1, null, true); + if (createContentTypes) + { + var contentTypeService = ctx.Application.Services.ContentTypeService; + var baseType = new ContentType(-1) {Alias = "base", Name = "Base"}; + var inheritedType = new ContentType(baseType) {Alias = "inherited", Name = "Inherited"}; + contentTypeService.Save(baseType); + contentTypeService.Save(inheritedType); + createContentTypes = false; + } + #region setup xml content + xmlContent = @" + + +]> + + +"; + #endregion + } + } +} diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 7bcb485e36..703bfde6b1 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -1,309 +1,309 @@ -using System.Linq; -using System.Collections.ObjectModel; -using System.Web.Routing; -using NUnit.Framework; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.ObjectResolution; -using Umbraco.Core.PropertyEditors; -using Umbraco.Web; -using Umbraco.Tests.TestHelpers; -using umbraco.BusinessLogic; -using Umbraco.Web.PublishedCache.XmlPublishedCache; -using Umbraco.Web.Security; - -namespace Umbraco.Tests.PublishedContent -{ - [TestFixture] - public class PublishedContentMoreTests : PublishedContentTestBase - { - protected override DatabaseBehavior DatabaseTestBehavior - { - get { return DatabaseBehavior.NoDatabasePerFixture; } - } - - // read http://stackoverflow.com/questions/7713326/extension-method-that-works-on-ienumerablet-and-iqueryablet - // and http://msmvps.com/blogs/jon_skeet/archive/2010/10/28/overloading-and-generic-constraints.aspx - // and http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx - - private PluginManager _pluginManager; - - public override void Initialize() - { - base.Initialize(); - - // this is so the model factory looks into the test assembly - _pluginManager = PluginManager.Current; - PluginManager.Current = new PluginManager(false) - { - AssembliesToScan = _pluginManager.AssembliesToScan - .Union(new[] { typeof (PublishedContentMoreTests).Assembly}) - }; - - InitializeUmbracoContext(); - } - - protected override void FreezeResolution() - { - PropertyValueConvertersResolver.Current = - new PropertyValueConvertersResolver(); - PublishedContentModelFactoryResolver.Current = - new PublishedContentModelFactoryResolver(new PublishedContentModelFactoryImpl()); - - base.FreezeResolution(); - } - - private void InitializeUmbracoContext() - { - RouteData routeData = null; - - var caches = CreatePublishedContent(); - - var httpContext = GetHttpContextFactory("http://umbraco.local/", routeData).HttpContext; - var ctx = new UmbracoContext( - httpContext, - ApplicationContext, - caches, - new WebSecurity(httpContext, ApplicationContext)); - - UmbracoContext.Current = ctx; - } - - public override void TearDown() - { - PluginManager.Current = _pluginManager; - ApplicationContext.Current.DisposeIfDisposable(); - ApplicationContext.Current = null; - } - - [Test] - public void First() - { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); - Assert.AreEqual("Content 1", content.Name); - } - - [Test] - public void DefaultContentSetIsSiblings() - { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); - Assert.AreEqual(0, content.Index()); - Assert.IsTrue(content.IsFirst()); - } - - [Test] - public void RunOnLatestContentSet() - { - // get first content - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); - var id = content.Id; - Assert.IsTrue(content.IsFirst()); - - // reverse => should be last, but set has not changed => still first - content = UmbracoContext.Current.ContentCache.GetAtRoot().Reverse().First(x => x.Id == id); - Assert.IsTrue(content.IsFirst()); - Assert.IsFalse(content.IsLast()); - - // reverse + new set => now it's last - content = UmbracoContext.Current.ContentCache.GetAtRoot().Reverse().ToContentSet().First(x => x.Id == id); - Assert.IsFalse(content.IsFirst()); - Assert.IsTrue(content.IsLast()); - - // reverse that set => should be first, but no new set => still last - content = UmbracoContext.Current.ContentCache.GetAtRoot().Reverse().ToContentSet().Reverse().First(x => x.Id == id); - Assert.IsFalse(content.IsFirst()); - Assert.IsTrue(content.IsLast()); - } - - [Test] - public void Distinct() - { - var content = UmbracoContext.Current.ContentCache.GetAtRoot() - .Distinct() - .Distinct() - .ToContentSet() - .First(); - - Assert.AreEqual("Content 1", content.Name); - Assert.IsTrue(content.IsFirst()); - Assert.IsFalse(content.IsLast()); - - content = content.Next(); - Assert.AreEqual("Content 2", content.Name); - Assert.IsFalse(content.IsFirst()); - Assert.IsFalse(content.IsLast()); - - content = content.Next(); - Assert.AreEqual("Content 2Sub", content.Name); - Assert.IsFalse(content.IsFirst()); - Assert.IsTrue(content.IsLast()); - } - - [Test] - [Ignore("Fails as long as PublishedContentModel is internal.")] // fixme - public void OfType1() - { - var content = UmbracoContext.Current.ContentCache.GetAtRoot() - .OfType() - .Distinct() - .ToArray(); - Assert.AreEqual(2, content.Count()); - Assert.IsInstanceOf(content.First()); - var set = content.ToContentSet(); - Assert.IsInstanceOf(set.First()); - Assert.AreSame(set, set.First().ContentSet); - Assert.IsInstanceOf(set.First().Next()); - } - - [Test] - [Ignore("Fails as long as PublishedContentModel is internal.")] // fixme - public void OfType2() - { - var content = UmbracoContext.Current.ContentCache.GetAtRoot() - .OfType() - .Distinct() - .ToArray(); - Assert.AreEqual(1, content.Count()); - Assert.IsInstanceOf(content.First()); - var set = content.ToContentSet(); - Assert.IsInstanceOf(set.First()); - } - - [Test] - [Ignore("Fails as long as PublishedContentModel is internal.")] // fixme - public void OfType() - { - var content = UmbracoContext.Current.ContentCache.GetAtRoot() - .OfType() - .First(x => x.Prop1 == 1234); - Assert.AreEqual("Content 2", content.Name); - Assert.AreEqual(1234, content.Prop1); - } - - [Test] - public void Position() - { - var content = UmbracoContext.Current.ContentCache.GetAtRoot() - .Where(x => x.GetPropertyValue("prop1") == 1234) - .ToContentSet() - .ToArray(); - - Assert.IsTrue(content.First().IsFirst()); - Assert.IsFalse(content.First().IsLast()); - Assert.IsFalse(content.First().Next().IsFirst()); - Assert.IsFalse(content.First().Next().IsLast()); - Assert.IsFalse(content.First().Next().Next().IsFirst()); - Assert.IsTrue(content.First().Next().Next().IsLast()); - } - - [Test] - [Ignore("Fails as long as PublishedContentModel is internal.")] // fixme - public void Issue() - { - var content = UmbracoContext.Current.ContentCache.GetAtRoot() - .Distinct() - .OfType(); - - var where = content.Where(x => x.Prop1 == 1234); - var first = where.First(); - Assert.AreEqual(1234, first.Prop1); - - var content2 = UmbracoContext.Current.ContentCache.GetAtRoot() - .OfType() - .First(x => x.Prop1 == 1234); - Assert.AreEqual(1234, content2.Prop1); - - var content3 = UmbracoContext.Current.ContentCache.GetAtRoot() - .OfType() - .First(); - Assert.AreEqual(1234, content3.Prop1); - } - - static SolidPublishedCaches CreatePublishedContent() - { - var caches = new SolidPublishedCaches(); - var cache = caches.ContentCache; - - var props = new[] - { - new PublishedPropertyType("prop1", 1, "?"), - }; - - var contentType1 = new PublishedContentType(1, "ContentType1", props); - var contentType2 = new PublishedContentType(2, "ContentType2", props); - var contentType2s = new PublishedContentType(3, "ContentType2Sub", props); - - cache.Add(new SolidPublishedContent(contentType1) - { - Id = 1, - SortOrder = 0, - Name = "Content 1", - UrlName = "content-1", - Path = "/1", - Level = 1, - Url = "/content-1", - ParentId = -1, - ChildIds = new int[] {}, - Properties = new Collection - { - new SolidPublishedProperty - { - PropertyTypeAlias = "prop1", - HasValue = true, - Value = 1234, - DataValue = "1234" - } - } - }); - - cache.Add(new SolidPublishedContent(contentType2) - { - Id = 2, - SortOrder = 1, - Name = "Content 2", - UrlName = "content-2", - Path = "/2", - Level = 1, - Url = "/content-2", - ParentId = -1, - ChildIds = new int[] { }, - Properties = new Collection - { - new SolidPublishedProperty - { - PropertyTypeAlias = "prop1", - HasValue = true, - Value = 1234, - DataValue = "1234" - } - } - }); - - cache.Add(new SolidPublishedContent(contentType2s) - { - Id = 3, - SortOrder = 2, - Name = "Content 2Sub", - UrlName = "content-2sub", - Path = "/3", - Level = 1, - Url = "/content-2sub", - ParentId = -1, - ChildIds = new int[] { }, - Properties = new Collection - { - new SolidPublishedProperty - { - PropertyTypeAlias = "prop1", - HasValue = true, - Value = 1234, - DataValue = "1234" - } - } - }); - - return caches; - } - } -} +using System.Linq; +using System.Collections.ObjectModel; +using System.Web.Routing; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.ObjectResolution; +using Umbraco.Core.PropertyEditors; +using Umbraco.Web; +using Umbraco.Tests.TestHelpers; +using umbraco.BusinessLogic; +using Umbraco.Web.PublishedCache.XmlPublishedCache; +using Umbraco.Web.Security; + +namespace Umbraco.Tests.PublishedContent +{ + [TestFixture] + public class PublishedContentMoreTests : PublishedContentTestBase + { + protected override DatabaseBehavior DatabaseTestBehavior + { + get { return DatabaseBehavior.NoDatabasePerFixture; } + } + + // read http://stackoverflow.com/questions/7713326/extension-method-that-works-on-ienumerablet-and-iqueryablet + // and http://msmvps.com/blogs/jon_skeet/archive/2010/10/28/overloading-and-generic-constraints.aspx + // and http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx + + private PluginManager _pluginManager; + + public override void Initialize() + { + base.Initialize(); + + // this is so the model factory looks into the test assembly + _pluginManager = PluginManager.Current; + PluginManager.Current = new PluginManager(false) + { + AssembliesToScan = _pluginManager.AssembliesToScan + .Union(new[] { typeof (PublishedContentMoreTests).Assembly}) + }; + + InitializeUmbracoContext(); + } + + protected override void FreezeResolution() + { + PropertyValueConvertersResolver.Current = + new PropertyValueConvertersResolver(); + PublishedContentModelFactoryResolver.Current = + new PublishedContentModelFactoryResolver(new PublishedContentModelFactoryImpl()); + + base.FreezeResolution(); + } + + private void InitializeUmbracoContext() + { + RouteData routeData = null; + + var caches = CreatePublishedContent(); + + var httpContext = GetHttpContextFactory("http://umbraco.local/", routeData).HttpContext; + var ctx = new UmbracoContext( + httpContext, + ApplicationContext, + caches, + new WebSecurity(httpContext, ApplicationContext)); + + UmbracoContext.Current = ctx; + } + + public override void TearDown() + { + PluginManager.Current = _pluginManager; + ApplicationContext.Current.DisposeIfDisposable(); + ApplicationContext.Current = null; + } + + [Test] + public void First() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + Assert.AreEqual("Content 1", content.Name); + } + + [Test] + public void DefaultContentSetIsSiblings() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + Assert.AreEqual(0, content.Index()); + Assert.IsTrue(content.IsFirst()); + } + + [Test] + public void RunOnLatestContentSet() + { + // get first content + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var id = content.Id; + Assert.IsTrue(content.IsFirst()); + + // reverse => should be last, but set has not changed => still first + content = UmbracoContext.Current.ContentCache.GetAtRoot().Reverse().First(x => x.Id == id); + Assert.IsTrue(content.IsFirst()); + Assert.IsFalse(content.IsLast()); + + // reverse + new set => now it's last + content = UmbracoContext.Current.ContentCache.GetAtRoot().Reverse().ToContentSet().First(x => x.Id == id); + Assert.IsFalse(content.IsFirst()); + Assert.IsTrue(content.IsLast()); + + // reverse that set => should be first, but no new set => still last + content = UmbracoContext.Current.ContentCache.GetAtRoot().Reverse().ToContentSet().Reverse().First(x => x.Id == id); + Assert.IsFalse(content.IsFirst()); + Assert.IsTrue(content.IsLast()); + } + + [Test] + public void Distinct() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot() + .Distinct() + .Distinct() + .ToContentSet() + .First(); + + Assert.AreEqual("Content 1", content.Name); + Assert.IsTrue(content.IsFirst()); + Assert.IsFalse(content.IsLast()); + + content = content.Next(); + Assert.AreEqual("Content 2", content.Name); + Assert.IsFalse(content.IsFirst()); + Assert.IsFalse(content.IsLast()); + + content = content.Next(); + Assert.AreEqual("Content 2Sub", content.Name); + Assert.IsFalse(content.IsFirst()); + Assert.IsTrue(content.IsLast()); + } + + [Test] + [Ignore("Fails as long as PublishedContentModel is internal.")] // fixme + public void OfType1() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot() + .OfType() + .Distinct() + .ToArray(); + Assert.AreEqual(2, content.Count()); + Assert.IsInstanceOf(content.First()); + var set = content.ToContentSet(); + Assert.IsInstanceOf(set.First()); + Assert.AreSame(set, set.First().ContentSet); + Assert.IsInstanceOf(set.First().Next()); + } + + [Test] + [Ignore("Fails as long as PublishedContentModel is internal.")] // fixme + public void OfType2() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot() + .OfType() + .Distinct() + .ToArray(); + Assert.AreEqual(1, content.Count()); + Assert.IsInstanceOf(content.First()); + var set = content.ToContentSet(); + Assert.IsInstanceOf(set.First()); + } + + [Test] + [Ignore("Fails as long as PublishedContentModel is internal.")] // fixme + public void OfType() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot() + .OfType() + .First(x => x.Prop1 == 1234); + Assert.AreEqual("Content 2", content.Name); + Assert.AreEqual(1234, content.Prop1); + } + + [Test] + public void Position() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot() + .Where(x => x.GetPropertyValue("prop1") == 1234) + .ToContentSet() + .ToArray(); + + Assert.IsTrue(content.First().IsFirst()); + Assert.IsFalse(content.First().IsLast()); + Assert.IsFalse(content.First().Next().IsFirst()); + Assert.IsFalse(content.First().Next().IsLast()); + Assert.IsFalse(content.First().Next().Next().IsFirst()); + Assert.IsTrue(content.First().Next().Next().IsLast()); + } + + [Test] + [Ignore("Fails as long as PublishedContentModel is internal.")] // fixme + public void Issue() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot() + .Distinct() + .OfType(); + + var where = content.Where(x => x.Prop1 == 1234); + var first = where.First(); + Assert.AreEqual(1234, first.Prop1); + + var content2 = UmbracoContext.Current.ContentCache.GetAtRoot() + .OfType() + .First(x => x.Prop1 == 1234); + Assert.AreEqual(1234, content2.Prop1); + + var content3 = UmbracoContext.Current.ContentCache.GetAtRoot() + .OfType() + .First(); + Assert.AreEqual(1234, content3.Prop1); + } + + static SolidPublishedCaches CreatePublishedContent() + { + var caches = new SolidPublishedCaches(); + var cache = caches.ContentCache; + + var props = new[] + { + new PublishedPropertyType("prop1", 1, "?"), + }; + + var contentType1 = new PublishedContentType(1, "ContentType1", props); + var contentType2 = new PublishedContentType(2, "ContentType2", props); + var contentType2s = new PublishedContentType(3, "ContentType2Sub", props); + + cache.Add(new SolidPublishedContent(contentType1) + { + Id = 1, + SortOrder = 0, + Name = "Content 1", + UrlName = "content-1", + Path = "/1", + Level = 1, + Url = "/content-1", + ParentId = -1, + ChildIds = new int[] {}, + Properties = new Collection + { + new SolidPublishedProperty + { + PropertyTypeAlias = "prop1", + HasValue = true, + Value = 1234, + DataValue = "1234" + } + } + }); + + cache.Add(new SolidPublishedContent(contentType2) + { + Id = 2, + SortOrder = 1, + Name = "Content 2", + UrlName = "content-2", + Path = "/2", + Level = 1, + Url = "/content-2", + ParentId = -1, + ChildIds = new int[] { }, + Properties = new Collection + { + new SolidPublishedProperty + { + PropertyTypeAlias = "prop1", + HasValue = true, + Value = 1234, + DataValue = "1234" + } + } + }); + + cache.Add(new SolidPublishedContent(contentType2s) + { + Id = 3, + SortOrder = 2, + Name = "Content 2Sub", + UrlName = "content-2sub", + Path = "/3", + Level = 1, + Url = "/content-2sub", + ParentId = -1, + ChildIds = new int[] { }, + Properties = new Collection + { + new SolidPublishedProperty + { + PropertyTypeAlias = "prop1", + HasValue = true, + Value = 1234, + DataValue = "1234" + } + } + }); + + return caches; + } + } +} diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs index bcb285822e..f7e41afb92 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs @@ -1,301 +1,301 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Web; -using Umbraco.Web.PublishedCache; - -namespace Umbraco.Tests.PublishedContent -{ - class SolidPublishedCaches : IPublishedCaches - { - public readonly SolidPublishedContentCache ContentCache = new SolidPublishedContentCache(); - - public ContextualPublishedContentCache CreateContextualContentCache(UmbracoContext context) - { - return new ContextualPublishedContentCache(ContentCache, context); - } - - public ContextualPublishedMediaCache CreateContextualMediaCache(UmbracoContext context) - { - return null; - } - } - - class SolidPublishedContentCache : IPublishedContentCache - { - private readonly Dictionary _content = new Dictionary(); - - public void Add(SolidPublishedContent content) - { - _content[content.Id] = PublishedContentModelFactory.CreateModel(content); - } - - public void Clear() - { - _content.Clear(); - } - - public void ContentHasChanged(UmbracoContext umbracoContext) - { - throw new NotImplementedException(); - } - - public IPublishedContent GetByRoute(UmbracoContext umbracoContext, bool preview, string route, bool? hideTopLevelNode = null) - { - throw new NotImplementedException(); - } - - public string GetRouteById(UmbracoContext umbracoContext, bool preview, int contentId) - { - throw new NotImplementedException(); - } - - public IPublishedContent GetById(UmbracoContext umbracoContext, bool preview, int contentId) - { - return _content.ContainsKey(contentId) ? _content[contentId] : null; - } - - public IEnumerable GetAtRoot(UmbracoContext umbracoContext, bool preview) - { - return _content.Values.Where(x => x.Parent == null); - } - - public IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, string xpath, Core.Xml.XPathVariable[] vars) - { - throw new NotImplementedException(); - } - - public IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, System.Xml.XPath.XPathExpression xpath, Core.Xml.XPathVariable[] vars) - { - throw new NotImplementedException(); - } - - public IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, string xpath, Core.Xml.XPathVariable[] vars) - { - throw new NotImplementedException(); - } - - public IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, System.Xml.XPath.XPathExpression xpath, Core.Xml.XPathVariable[] vars) - { - throw new NotImplementedException(); - } - - public System.Xml.XPath.XPathNavigator GetXPathNavigator(UmbracoContext umbracoContext, bool preview) - { - throw new NotImplementedException(); - } - - public bool XPathNavigatorIsNavigable - { - get { throw new NotImplementedException(); } - } - - public bool HasContent(UmbracoContext umbracoContext, bool preview) - { - return _content.Count > 0; - } - } - - class SolidPublishedContent : IPublishedContent - { - #region Constructor - - public SolidPublishedContent(PublishedContentType contentType) - { - // initialize boring stuff - TemplateId = 0; - WriterName = CreatorName = string.Empty; - WriterId = CreatorId = 0; - CreateDate = UpdateDate = DateTime.Now; - Version = Guid.Empty; - IsDraft = false; - - ContentType = contentType; - DocumentTypeAlias = contentType.Alias; - DocumentTypeId = contentType.Id; - } - - #endregion - - #region Content - - public int Id { get; set; } - public int TemplateId { get; set; } - public int SortOrder { get; set; } - public string Name { get; set; } - public string UrlName { get; set; } - public string DocumentTypeAlias { get; private set; } - public int DocumentTypeId { get; private set; } - public string WriterName { get; set; } - public string CreatorName { get; set; } - public int WriterId { get; set; } - public int CreatorId { get; set; } - public string Path { get; set; } - public DateTime CreateDate { get; set; } - public DateTime UpdateDate { get; set; } - public Guid Version { get; set; } - public int Level { get; set; } - public string Url { get; set; } - - public PublishedItemType ItemType { get { return PublishedItemType.Content; } } - public bool IsDraft { get; set; } - - public int GetIndex() - { - var index = this.Siblings().FindIndex(x => x.Id == Id); - if (index < 0) - throw new IndexOutOfRangeException("Failed to find content in its siblings collection?!"); - return index; - } - - #endregion - - #region Tree - - public int ParentId { get; set; } - public IEnumerable ChildIds { get; set; } - - public IPublishedContent Parent { get { return UmbracoContext.Current.ContentCache.GetById(ParentId); } } - public IEnumerable Children { get { return ChildIds.Select(id => UmbracoContext.Current.ContentCache.GetById(id)); } } - - #endregion - - #region ContentSet - - public IEnumerable ContentSet { get { return this.Siblings(); } } - - #endregion - - #region ContentType - - public PublishedContentType ContentType { get; private set; } - - #endregion - - #region Properties - - public ICollection Properties { get; set; } - - public IPublishedProperty GetProperty(string alias) - { - return Properties.FirstOrDefault(p => p.PropertyTypeAlias.InvariantEquals(alias)); - } - - public IPublishedProperty GetProperty(string alias, bool recurse) - { - var property = GetProperty(alias); - if (recurse == false) return property; - - IPublishedContent content = this; - while (content != null && (property == null || property.HasValue == false)) - { - content = content.Parent; - property = content == null ? null : content.GetProperty(alias); - } - - return property; - } - - public object this[string alias] - { - get - { - var property = GetProperty(alias); - return property == null || property.HasValue == false ? null : property.Value; - } - } - - #endregion - } - - class SolidPublishedProperty : IPublishedProperty - { - public SolidPublishedProperty() - { - // initialize boring stuff - } - - public string PropertyTypeAlias { get; set; } - public object DataValue { get; set; } - public object Value { get; set; } - public bool HasValue { get; set; } - public object XPathValue { get; set; } - } - - [PublishedContentModel("ContentType2")] - internal class ContentType2 : PublishedContentModel - { - #region Plumbing - - public ContentType2(IPublishedContent content) - : base(content) - { } - - #endregion - - // fast, if you know that the appropriate IPropertyEditorValueConverter is wired - public int Prop1 { get { return (int)this["prop1"]; } } - - // almost as fast, not sure I like it as much, though - //public int Prop1 { get { return this.GetPropertyValue("prop1"); } } - } - - [PublishedContentModel("ContentType2Sub")] - internal class ContentType2Sub : ContentType2 - { - #region Plumbing - - public ContentType2Sub(IPublishedContent content) - : base(content) - { } - - #endregion - } - - class PublishedContentStrong1 : PublishedContentExtended - { - public PublishedContentStrong1(IPublishedContent content) - : base(content) - { } - - public int StrongValue { get { return (int)this["strongValue"]; } } - } - - class PublishedContentStrong1Sub : PublishedContentStrong1 - { - public PublishedContentStrong1Sub(IPublishedContent content) - : base(content) - { } - - public int AnotherValue { get { return (int)this["anotherValue"]; } } - } - - class PublishedContentStrong2 : PublishedContentExtended - { - public PublishedContentStrong2(IPublishedContent content) - : base(content) - { } - - public int StrongValue { get { return (int)this["strongValue"]; } } - } - - class AutoPublishedContentType : PublishedContentType - { - private static readonly PublishedPropertyType Default = new PublishedPropertyType("*", 0, "?"); - - public AutoPublishedContentType(int id, string alias, IEnumerable propertyTypes) - : base(id, alias, propertyTypes) - { } - - public override PublishedPropertyType GetPropertyType(string alias) - { - var propertyType = base.GetPropertyType(alias); - return propertyType ?? Default; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web; +using Umbraco.Web.PublishedCache; + +namespace Umbraco.Tests.PublishedContent +{ + class SolidPublishedCaches : IPublishedCaches + { + public readonly SolidPublishedContentCache ContentCache = new SolidPublishedContentCache(); + + public ContextualPublishedContentCache CreateContextualContentCache(UmbracoContext context) + { + return new ContextualPublishedContentCache(ContentCache, context); + } + + public ContextualPublishedMediaCache CreateContextualMediaCache(UmbracoContext context) + { + return null; + } + } + + class SolidPublishedContentCache : IPublishedContentCache + { + private readonly Dictionary _content = new Dictionary(); + + public void Add(SolidPublishedContent content) + { + _content[content.Id] = PublishedContentModelFactory.CreateModel(content); + } + + public void Clear() + { + _content.Clear(); + } + + public void ContentHasChanged(UmbracoContext umbracoContext) + { + throw new NotImplementedException(); + } + + public IPublishedContent GetByRoute(UmbracoContext umbracoContext, bool preview, string route, bool? hideTopLevelNode = null) + { + throw new NotImplementedException(); + } + + public string GetRouteById(UmbracoContext umbracoContext, bool preview, int contentId) + { + throw new NotImplementedException(); + } + + public IPublishedContent GetById(UmbracoContext umbracoContext, bool preview, int contentId) + { + return _content.ContainsKey(contentId) ? _content[contentId] : null; + } + + public IEnumerable GetAtRoot(UmbracoContext umbracoContext, bool preview) + { + return _content.Values.Where(x => x.Parent == null); + } + + public IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, string xpath, Core.Xml.XPathVariable[] vars) + { + throw new NotImplementedException(); + } + + public IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, System.Xml.XPath.XPathExpression xpath, Core.Xml.XPathVariable[] vars) + { + throw new NotImplementedException(); + } + + public IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, string xpath, Core.Xml.XPathVariable[] vars) + { + throw new NotImplementedException(); + } + + public IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, System.Xml.XPath.XPathExpression xpath, Core.Xml.XPathVariable[] vars) + { + throw new NotImplementedException(); + } + + public System.Xml.XPath.XPathNavigator GetXPathNavigator(UmbracoContext umbracoContext, bool preview) + { + throw new NotImplementedException(); + } + + public bool XPathNavigatorIsNavigable + { + get { throw new NotImplementedException(); } + } + + public bool HasContent(UmbracoContext umbracoContext, bool preview) + { + return _content.Count > 0; + } + } + + class SolidPublishedContent : IPublishedContent + { + #region Constructor + + public SolidPublishedContent(PublishedContentType contentType) + { + // initialize boring stuff + TemplateId = 0; + WriterName = CreatorName = string.Empty; + WriterId = CreatorId = 0; + CreateDate = UpdateDate = DateTime.Now; + Version = Guid.Empty; + IsDraft = false; + + ContentType = contentType; + DocumentTypeAlias = contentType.Alias; + DocumentTypeId = contentType.Id; + } + + #endregion + + #region Content + + public int Id { get; set; } + public int TemplateId { get; set; } + public int SortOrder { get; set; } + public string Name { get; set; } + public string UrlName { get; set; } + public string DocumentTypeAlias { get; private set; } + public int DocumentTypeId { get; private set; } + public string WriterName { get; set; } + public string CreatorName { get; set; } + public int WriterId { get; set; } + public int CreatorId { get; set; } + public string Path { get; set; } + public DateTime CreateDate { get; set; } + public DateTime UpdateDate { get; set; } + public Guid Version { get; set; } + public int Level { get; set; } + public string Url { get; set; } + + public PublishedItemType ItemType { get { return PublishedItemType.Content; } } + public bool IsDraft { get; set; } + + public int GetIndex() + { + var index = this.Siblings().FindIndex(x => x.Id == Id); + if (index < 0) + throw new IndexOutOfRangeException("Failed to find content in its siblings collection?!"); + return index; + } + + #endregion + + #region Tree + + public int ParentId { get; set; } + public IEnumerable ChildIds { get; set; } + + public IPublishedContent Parent { get { return UmbracoContext.Current.ContentCache.GetById(ParentId); } } + public IEnumerable Children { get { return ChildIds.Select(id => UmbracoContext.Current.ContentCache.GetById(id)); } } + + #endregion + + #region ContentSet + + public IEnumerable ContentSet { get { return this.Siblings(); } } + + #endregion + + #region ContentType + + public PublishedContentType ContentType { get; private set; } + + #endregion + + #region Properties + + public ICollection Properties { get; set; } + + public IPublishedProperty GetProperty(string alias) + { + return Properties.FirstOrDefault(p => p.PropertyTypeAlias.InvariantEquals(alias)); + } + + public IPublishedProperty GetProperty(string alias, bool recurse) + { + var property = GetProperty(alias); + if (recurse == false) return property; + + IPublishedContent content = this; + while (content != null && (property == null || property.HasValue == false)) + { + content = content.Parent; + property = content == null ? null : content.GetProperty(alias); + } + + return property; + } + + public object this[string alias] + { + get + { + var property = GetProperty(alias); + return property == null || property.HasValue == false ? null : property.Value; + } + } + + #endregion + } + + class SolidPublishedProperty : IPublishedProperty + { + public SolidPublishedProperty() + { + // initialize boring stuff + } + + public string PropertyTypeAlias { get; set; } + public object DataValue { get; set; } + public object Value { get; set; } + public bool HasValue { get; set; } + public object XPathValue { get; set; } + } + + [PublishedContentModel("ContentType2")] + internal class ContentType2 : PublishedContentModel + { + #region Plumbing + + public ContentType2(IPublishedContent content) + : base(content) + { } + + #endregion + + // fast, if you know that the appropriate IPropertyEditorValueConverter is wired + public int Prop1 { get { return (int)this["prop1"]; } } + + // almost as fast, not sure I like it as much, though + //public int Prop1 { get { return this.GetPropertyValue("prop1"); } } + } + + [PublishedContentModel("ContentType2Sub")] + internal class ContentType2Sub : ContentType2 + { + #region Plumbing + + public ContentType2Sub(IPublishedContent content) + : base(content) + { } + + #endregion + } + + class PublishedContentStrong1 : PublishedContentExtended + { + public PublishedContentStrong1(IPublishedContent content) + : base(content) + { } + + public int StrongValue { get { return (int)this["strongValue"]; } } + } + + class PublishedContentStrong1Sub : PublishedContentStrong1 + { + public PublishedContentStrong1Sub(IPublishedContent content) + : base(content) + { } + + public int AnotherValue { get { return (int)this["anotherValue"]; } } + } + + class PublishedContentStrong2 : PublishedContentExtended + { + public PublishedContentStrong2(IPublishedContent content) + : base(content) + { } + + public int StrongValue { get { return (int)this["strongValue"]; } } + } + + class AutoPublishedContentType : PublishedContentType + { + private static readonly PublishedPropertyType Default = new PublishedPropertyType("*", 0, "?"); + + public AutoPublishedContentType(int id, string alias, IEnumerable propertyTypes) + : base(id, alias, propertyTypes) + { } + + public override PublishedPropertyType GetPropertyType(string alias) + { + var propertyType = base.GetPropertyType(alias); + return propertyType ?? Default; + } + } +} diff --git a/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/CallingMethodTests.cs b/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/CallingMethodTests.cs index eae6593e5d..2d03b02599 100644 --- a/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/CallingMethodTests.cs +++ b/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/CallingMethodTests.cs @@ -1,28 +1,28 @@ -using System; -using System.Reflection; -using NUnit.Framework; - -namespace Umbraco.Tests.PublishedContent.StronglyTypedModels -{ - [TestFixture] - public class CallingMethodTests - { - private readonly Func _myProperty = MethodBase.GetCurrentMethod; - - public string AField - { - get { return Resolve(_myProperty()); } - } - - private string Resolve(MethodBase m) - { - return m.Name.Replace("get_", ""); - } - - [Test] - public void GetMyName() - { - Assert.AreEqual("AField", AField); - } - } +using System; +using System.Reflection; +using NUnit.Framework; + +namespace Umbraco.Tests.PublishedContent.StronglyTypedModels +{ + [TestFixture] + public class CallingMethodTests + { + private readonly Func _myProperty = MethodBase.GetCurrentMethod; + + public string AField + { + get { return Resolve(_myProperty()); } + } + + private string Resolve(MethodBase m) + { + return m.Name.Replace("get_", ""); + } + + [Test] + public void GetMyName() + { + Assert.AreEqual("AField", AField); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/Subpage.cs b/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/Subpage.cs index 57d1ee3492..2bb16a6074 100644 --- a/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/Subpage.cs +++ b/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/Subpage.cs @@ -1,41 +1,41 @@ -using System.Collections.Generic; -using Umbraco.Core.Models; - -namespace Umbraco.Tests.PublishedContent.StronglyTypedModels -{ - /// - /// Represents a Subpage which acts as the strongly typed model for a Doc Type - /// with alias "Subpage" and "Subpage" as the allowed child type. - /// - /// Similar to the Textpage this model could also be generated, but it could also - /// act as a Code-First model by using the attributes shown on the various properties, - /// which decorate the model with information about the Document Type, its - /// Property Groups and Property Types. - /// - public class Subpage : TypedModelBase - { - public Subpage(IPublishedContent publishedContent) : base(publishedContent) - { - } - - public string Title { get { return Resolve(Property()); } } - - public string BodyText { get { return Resolve(Property()); } } - - public Textpage Parent - { - get - { - return Parent(); - } - } - - public IEnumerable Subpages - { - get - { - return Children(ContentTypeAlias()); - } - } - } +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Tests.PublishedContent.StronglyTypedModels +{ + /// + /// Represents a Subpage which acts as the strongly typed model for a Doc Type + /// with alias "Subpage" and "Subpage" as the allowed child type. + /// + /// Similar to the Textpage this model could also be generated, but it could also + /// act as a Code-First model by using the attributes shown on the various properties, + /// which decorate the model with information about the Document Type, its + /// Property Groups and Property Types. + /// + public class Subpage : TypedModelBase + { + public Subpage(IPublishedContent publishedContent) : base(publishedContent) + { + } + + public string Title { get { return Resolve(Property()); } } + + public string BodyText { get { return Resolve(Property()); } } + + public Textpage Parent + { + get + { + return Parent(); + } + } + + public IEnumerable Subpages + { + get + { + return Children(ContentTypeAlias()); + } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/Textpage.cs b/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/Textpage.cs index fff1ae9107..cf59c7c4f2 100644 --- a/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/Textpage.cs +++ b/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/Textpage.cs @@ -1,60 +1,60 @@ -using System; -using System.Collections.Generic; -using Umbraco.Core.Models; - -namespace Umbraco.Tests.PublishedContent.StronglyTypedModels -{ - /// - /// Represents a Textpage which acts as the strongly typed model for a Doc Type - /// with alias "Textpage" and "Subpage" as the allowed child type. - /// - /// The basic properties are resolved by convention using the Resolve-Type-PropertyTypeAlias - /// convention available through the base class' protected Resolve-method and Property-delegate. - /// - /// The Textpage allows the use of Subpage and Textpage as child doc types, which are exposed as a - /// collection using the Children-Type-ContentTypeAlias convention available through the - /// base class' protected Children-method and ContentTypeAlias-delegate. - /// - /// - /// This code can easily be generated using simple conventions for the types and names - /// of the properties that this type of strongly typed model exposes. - /// - public class Textpage : TypedModelBase - { - public Textpage(IPublishedContent publishedContent) : base(publishedContent) - { - } - - public string Title { get { return Resolve(Property()); } } - - public string BodyText { get { return Resolve(Property()); } } - - public string AuthorName { get { return Resolve(Property()); } } - - public DateTime Date { get { return Resolve(Property()); } } - - public Textpage Parent - { - get - { - return Parent(); - } - } - - public IEnumerable Textpages - { - get - { - return Children(ContentTypeAlias()); - } - } - - public IEnumerable Subpages - { - get - { - return Children(ContentTypeAlias()); - } - } - } +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Tests.PublishedContent.StronglyTypedModels +{ + /// + /// Represents a Textpage which acts as the strongly typed model for a Doc Type + /// with alias "Textpage" and "Subpage" as the allowed child type. + /// + /// The basic properties are resolved by convention using the Resolve-Type-PropertyTypeAlias + /// convention available through the base class' protected Resolve-method and Property-delegate. + /// + /// The Textpage allows the use of Subpage and Textpage as child doc types, which are exposed as a + /// collection using the Children-Type-ContentTypeAlias convention available through the + /// base class' protected Children-method and ContentTypeAlias-delegate. + /// + /// + /// This code can easily be generated using simple conventions for the types and names + /// of the properties that this type of strongly typed model exposes. + /// + public class Textpage : TypedModelBase + { + public Textpage(IPublishedContent publishedContent) : base(publishedContent) + { + } + + public string Title { get { return Resolve(Property()); } } + + public string BodyText { get { return Resolve(Property()); } } + + public string AuthorName { get { return Resolve(Property()); } } + + public DateTime Date { get { return Resolve(Property()); } } + + public Textpage Parent + { + get + { + return Parent(); + } + } + + public IEnumerable Textpages + { + get + { + return Children(ContentTypeAlias()); + } + } + + public IEnumerable Subpages + { + get + { + return Children(ContentTypeAlias()); + } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/TypedModelBase.cs b/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/TypedModelBase.cs index f5df693982..0de99c76ad 100644 --- a/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/TypedModelBase.cs +++ b/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/TypedModelBase.cs @@ -1,170 +1,170 @@ -using System; -using System.Collections.Generic; -using System.Data.Entity.Design.PluralizationServices; -using System.Globalization; -using System.Reflection; -using System.Linq; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Web; - -namespace Umbraco.Tests.PublishedContent.StronglyTypedModels -{ - /// - /// Represents the abstract base class for a 'TypedModel', which basically wraps IPublishedContent - /// underneath a strongly typed model like "Textpage" and "Subpage". - /// Because IPublishedContent is used under the hood there is no need for additional mapping, so the - /// only added cost should be the creation of the objects, which the IPublishedContent instance is - /// passed into. - /// - /// This base class exposes a simple way to write property getters by convention without - /// using the string alias of a PropertyType (this is resolved by the use of the Property delegate). - /// - /// This base class also exposes query options like Parent, Children, Ancestors and Descendants, - /// which can be used for collections of strongly typed child models/objects. These types of collections - /// typically corresponds to 'allowed child content types' on a Doc Type (at different levels). - /// - /// The IPublishedContent properties are also exposed through this base class, but only - /// by casting the typed model to IPublishedContent, so the properties doesn't show up by default: - /// ie. ((IPublishedContent)textpage).Url - /// - public abstract class TypedModelBase : PublishedContentWrapped // IPublishedContent - { - protected TypedModelBase(IPublishedContent publishedContent) - : base(publishedContent) - { } - - protected readonly Func Property = MethodBase.GetCurrentMethod; - protected readonly Func ContentTypeAlias = MethodBase.GetCurrentMethod; - - #region Properties - - protected T Resolve(MethodBase methodBase) - { - var propertyTypeAlias = methodBase.ToUmbracoAlias(); - return Resolve(propertyTypeAlias); - } - - protected T Resolve(string propertyTypeAlias) - { - return Content.GetPropertyValue(propertyTypeAlias); - } - - protected T Resolve(MethodBase methodBase, T ifCannotConvert) - { - var propertyTypeAlias = methodBase.ToUmbracoAlias(); - return Resolve(propertyTypeAlias, ifCannotConvert); - } - - protected T Resolve(string propertyTypeAlias, T ifCannotConvert) - { - return Content.GetPropertyValue(propertyTypeAlias, false, ifCannotConvert); - } - - protected T Resolve(MethodBase methodBase, bool recursive, T ifCannotConvert) - { - var propertyTypeAlias = methodBase.ToUmbracoAlias(); - return Resolve(propertyTypeAlias, recursive, ifCannotConvert); - } - - protected T Resolve(string propertyTypeAlias, bool recursive, T ifCannotConvert) - { - return Content.GetPropertyValue(propertyTypeAlias, recursive, ifCannotConvert); - } - #endregion - - #region Querying - protected T Parent() where T : TypedModelBase - { - var constructorInfo = typeof(T).GetConstructor(new[] { typeof(IPublishedContent) }); - if (constructorInfo == null) - throw new Exception("No valid constructor found"); - - return (T) constructorInfo.Invoke(new object[] {Content.Parent}); - } - - protected IEnumerable Children(MethodBase methodBase) where T : TypedModelBase - { - var docTypeAlias = methodBase.CleanCallingMethodName(); - return Children(docTypeAlias); - } - - protected IEnumerable Children(string docTypeAlias) where T : TypedModelBase - { - var constructorInfo = typeof(T).GetConstructor(new[] { typeof(IPublishedContent) }); - if(constructorInfo == null) - throw new Exception("No valid constructor found"); - - string singularizedDocTypeAlias = docTypeAlias.ToSingular(); - - return Content.Children.Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias) - .Select(x => (T)constructorInfo.Invoke(new object[] { x })); - } - - protected IEnumerable Ancestors(MethodBase methodBase) where T : TypedModelBase - { - var docTypeAlias = methodBase.CleanCallingMethodName(); - return Ancestors(docTypeAlias); - } - - protected IEnumerable Ancestors(string docTypeAlias) where T : TypedModelBase - { - var constructorInfo = typeof(T).GetConstructor(new[] { typeof(IPublishedContent) }); - if (constructorInfo == null) - throw new Exception("No valid constructor found"); - - string singularizedDocTypeAlias = docTypeAlias.ToSingular(); - - return Content.Ancestors().Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias) - .Select(x => (T)constructorInfo.Invoke(new object[] { x })); - } - - protected IEnumerable Descendants(MethodBase methodBase) where T : TypedModelBase - { - var docTypeAlias = methodBase.CleanCallingMethodName(); - return Descendants(docTypeAlias); - } - - protected IEnumerable Descendants(string docTypeAlias) where T : TypedModelBase - { - var constructorInfo = typeof(T).GetConstructor(new[] { typeof(IPublishedContent) }); - if (constructorInfo == null) - throw new Exception("No valid constructor found"); - - string singularizedDocTypeAlias = docTypeAlias.ToSingular(); - - return Content.Descendants().Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias) - .Select(x => (T)constructorInfo.Invoke(new object[] { x })); - } - #endregion - } - - /// - /// Extension methods for MethodBase, which are used to clean the name of the calling method "get_BodyText" - /// to "BodyText" and then make it camel case according to the UmbracoAlias convention "bodyText". - /// There is also a string extension for making plural words singular, which is used when going from - /// something like "Subpages" to "Subpage" for Children/Ancestors/Descendants Doc Type aliases. - /// - public static class TypeExtensions - { - public static string CleanCallingMethodName(this MethodBase methodBase) - { - return methodBase.Name.Replace("get_", ""); - } - - public static string ToUmbracoAlias(this MethodBase methodBase) - { - return methodBase.CleanCallingMethodName().ToUmbracoAlias(); - } - - public static string ToSingular(this string pluralWord) - { - var service = PluralizationService.CreateService(new CultureInfo("en-US")); - if (service.IsPlural(pluralWord)) - return service.Singularize(pluralWord); - - return pluralWord; - } - } +using System; +using System.Collections.Generic; +using System.Data.Entity.Design.PluralizationServices; +using System.Globalization; +using System.Reflection; +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web; + +namespace Umbraco.Tests.PublishedContent.StronglyTypedModels +{ + /// + /// Represents the abstract base class for a 'TypedModel', which basically wraps IPublishedContent + /// underneath a strongly typed model like "Textpage" and "Subpage". + /// Because IPublishedContent is used under the hood there is no need for additional mapping, so the + /// only added cost should be the creation of the objects, which the IPublishedContent instance is + /// passed into. + /// + /// This base class exposes a simple way to write property getters by convention without + /// using the string alias of a PropertyType (this is resolved by the use of the Property delegate). + /// + /// This base class also exposes query options like Parent, Children, Ancestors and Descendants, + /// which can be used for collections of strongly typed child models/objects. These types of collections + /// typically corresponds to 'allowed child content types' on a Doc Type (at different levels). + /// + /// The IPublishedContent properties are also exposed through this base class, but only + /// by casting the typed model to IPublishedContent, so the properties doesn't show up by default: + /// ie. ((IPublishedContent)textpage).Url + /// + public abstract class TypedModelBase : PublishedContentWrapped // IPublishedContent + { + protected TypedModelBase(IPublishedContent publishedContent) + : base(publishedContent) + { } + + protected readonly Func Property = MethodBase.GetCurrentMethod; + protected readonly Func ContentTypeAlias = MethodBase.GetCurrentMethod; + + #region Properties + + protected T Resolve(MethodBase methodBase) + { + var propertyTypeAlias = methodBase.ToUmbracoAlias(); + return Resolve(propertyTypeAlias); + } + + protected T Resolve(string propertyTypeAlias) + { + return Content.GetPropertyValue(propertyTypeAlias); + } + + protected T Resolve(MethodBase methodBase, T ifCannotConvert) + { + var propertyTypeAlias = methodBase.ToUmbracoAlias(); + return Resolve(propertyTypeAlias, ifCannotConvert); + } + + protected T Resolve(string propertyTypeAlias, T ifCannotConvert) + { + return Content.GetPropertyValue(propertyTypeAlias, false, ifCannotConvert); + } + + protected T Resolve(MethodBase methodBase, bool recursive, T ifCannotConvert) + { + var propertyTypeAlias = methodBase.ToUmbracoAlias(); + return Resolve(propertyTypeAlias, recursive, ifCannotConvert); + } + + protected T Resolve(string propertyTypeAlias, bool recursive, T ifCannotConvert) + { + return Content.GetPropertyValue(propertyTypeAlias, recursive, ifCannotConvert); + } + #endregion + + #region Querying + protected T Parent() where T : TypedModelBase + { + var constructorInfo = typeof(T).GetConstructor(new[] { typeof(IPublishedContent) }); + if (constructorInfo == null) + throw new Exception("No valid constructor found"); + + return (T) constructorInfo.Invoke(new object[] {Content.Parent}); + } + + protected IEnumerable Children(MethodBase methodBase) where T : TypedModelBase + { + var docTypeAlias = methodBase.CleanCallingMethodName(); + return Children(docTypeAlias); + } + + protected IEnumerable Children(string docTypeAlias) where T : TypedModelBase + { + var constructorInfo = typeof(T).GetConstructor(new[] { typeof(IPublishedContent) }); + if(constructorInfo == null) + throw new Exception("No valid constructor found"); + + string singularizedDocTypeAlias = docTypeAlias.ToSingular(); + + return Content.Children.Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias) + .Select(x => (T)constructorInfo.Invoke(new object[] { x })); + } + + protected IEnumerable Ancestors(MethodBase methodBase) where T : TypedModelBase + { + var docTypeAlias = methodBase.CleanCallingMethodName(); + return Ancestors(docTypeAlias); + } + + protected IEnumerable Ancestors(string docTypeAlias) where T : TypedModelBase + { + var constructorInfo = typeof(T).GetConstructor(new[] { typeof(IPublishedContent) }); + if (constructorInfo == null) + throw new Exception("No valid constructor found"); + + string singularizedDocTypeAlias = docTypeAlias.ToSingular(); + + return Content.Ancestors().Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias) + .Select(x => (T)constructorInfo.Invoke(new object[] { x })); + } + + protected IEnumerable Descendants(MethodBase methodBase) where T : TypedModelBase + { + var docTypeAlias = methodBase.CleanCallingMethodName(); + return Descendants(docTypeAlias); + } + + protected IEnumerable Descendants(string docTypeAlias) where T : TypedModelBase + { + var constructorInfo = typeof(T).GetConstructor(new[] { typeof(IPublishedContent) }); + if (constructorInfo == null) + throw new Exception("No valid constructor found"); + + string singularizedDocTypeAlias = docTypeAlias.ToSingular(); + + return Content.Descendants().Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias) + .Select(x => (T)constructorInfo.Invoke(new object[] { x })); + } + #endregion + } + + /// + /// Extension methods for MethodBase, which are used to clean the name of the calling method "get_BodyText" + /// to "BodyText" and then make it camel case according to the UmbracoAlias convention "bodyText". + /// There is also a string extension for making plural words singular, which is used when going from + /// something like "Subpages" to "Subpage" for Children/Ancestors/Descendants Doc Type aliases. + /// + public static class TypeExtensions + { + public static string CleanCallingMethodName(this MethodBase methodBase) + { + return methodBase.Name.Replace("get_", ""); + } + + public static string ToUmbracoAlias(this MethodBase methodBase) + { + return methodBase.CleanCallingMethodName().ToUmbracoAlias(); + } + + public static string ToSingular(this string pluralWord) + { + var service = PluralizationService.CreateService(new CultureInfo("en-US")); + if (service.IsPlural(pluralWord)) + return service.Singularize(pluralWord); + + return pluralWord; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/UmbracoTemplatePage`T.cs b/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/UmbracoTemplatePage`T.cs index ae0a3c062e..4d3dbf642c 100644 --- a/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/UmbracoTemplatePage`T.cs +++ b/src/Umbraco.Tests/PublishedContent/StronglyTypedModels/UmbracoTemplatePage`T.cs @@ -1,35 +1,35 @@ -using Umbraco.Core.Models; -using Umbraco.Web.Mvc; - -namespace Umbraco.Tests.PublishedContent.StronglyTypedModels -{ - /// - /// Represents a basic extension of the UmbracoTemplatePage, which allows you to specify - /// the type of a strongly typed model that inherits from TypedModelBase. - /// The model is exposed as TypedModel. - /// - /// Type of the model to create/expose - public abstract class UmbracoTemplatePage : UmbracoTemplatePage where T : TypedModelBase - { - protected override void InitializePage() - { - base.InitializePage(); - - //set the model to the current node if it is not set, this is generally not the case - if (Model != null) - { - //Map CurrentModel here - var constructorInfo = typeof(T).GetConstructor(new []{typeof(IPublishedContent)}); - if (constructorInfo != null) - { - TypedModel = constructorInfo.Invoke(new object[]{Model.Content}) as T; - } - } - } - - /// - /// Returns the a strongly typed model - /// - public T TypedModel { get; private set; } - } +using Umbraco.Core.Models; +using Umbraco.Web.Mvc; + +namespace Umbraco.Tests.PublishedContent.StronglyTypedModels +{ + /// + /// Represents a basic extension of the UmbracoTemplatePage, which allows you to specify + /// the type of a strongly typed model that inherits from TypedModelBase. + /// The model is exposed as TypedModel. + /// + /// Type of the model to create/expose + public abstract class UmbracoTemplatePage : UmbracoTemplatePage where T : TypedModelBase + { + protected override void InitializePage() + { + base.InitializePage(); + + //set the model to the current node if it is not set, this is generally not the case + if (Model != null) + { + //Map CurrentModel here + var constructorInfo = typeof(T).GetConstructor(new []{typeof(IPublishedContent)}); + if (constructorInfo != null) + { + TypedModel = constructorInfo.Invoke(new object[]{Model.Content}) as T; + } + } + } + + /// + /// Returns the a strongly typed model + /// + public T TypedModel { get; private set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Services/Importing/CheckboxList-Content-Package-LegacyIds.xml b/src/Umbraco.Tests/Services/Importing/CheckboxList-Content-Package-LegacyIds.xml index a0f4448393..8a8380cf65 100644 --- a/src/Umbraco.Tests/Services/Importing/CheckboxList-Content-Package-LegacyIds.xml +++ b/src/Umbraco.Tests/Services/Importing/CheckboxList-Content-Package-LegacyIds.xml @@ -1,76 +1,76 @@ - - - - - - CheckboxListTest - 1 - MIT license - 1 - - 3 - 0 - 0 - - - - 1 - 1 - - - - - - - - - - - - - - NewType - NewType - .sprTreeFolder - folder.png - - - True - - - - NewType - - - - - testList - testList - b4471851-82b6-4c75-afa4-39fa9c6a75e9 - 8dacee1a-1bbd-46c0-b07f-8ffd49fcca7c - - - False - - - - - - - - - - - - - - - - - - - - - - + + + + + + CheckboxListTest + 1 + MIT license + 1 + + 3 + 0 + 0 + + + + 1 + 1 + + + + + + + + + + + + + + NewType + NewType + .sprTreeFolder + folder.png + + + True + + + + NewType + + + + + testList + testList + b4471851-82b6-4c75-afa4-39fa9c6a75e9 + 8dacee1a-1bbd-46c0-b07f-8ffd49fcca7c + + + False + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Tests/Services/LocalizationServiceTests.cs b/src/Umbraco.Tests/Services/LocalizationServiceTests.cs index fa5c6f3253..8b4a0ce0fa 100644 --- a/src/Umbraco.Tests/Services/LocalizationServiceTests.cs +++ b/src/Umbraco.Tests/Services/LocalizationServiceTests.cs @@ -1,25 +1,25 @@ -using NUnit.Framework; - -namespace Umbraco.Tests.Services -{ - /// - /// Tests covering all methods in the LocalizationService class. - /// This is more of an integration test as it involves multiple layers - /// as well as configuration. - /// - [TestFixture, RequiresSTA] - public class LocalizationServiceTests : BaseServiceTest - { - [SetUp] - public override void Initialize() - { - base.Initialize(); - } - - [TearDown] - public override void TearDown() - { - base.TearDown(); - } - } +using NUnit.Framework; + +namespace Umbraco.Tests.Services +{ + /// + /// Tests covering all methods in the LocalizationService class. + /// This is more of an integration test as it involves multiple layers + /// as well as configuration. + /// + [TestFixture, RequiresSTA] + public class LocalizationServiceTests : BaseServiceTest + { + [SetUp] + public override void Initialize() + { + base.Initialize(); + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Services/MacroServiceTests.cs b/src/Umbraco.Tests/Services/MacroServiceTests.cs index 28c62126e1..926702c8e4 100644 --- a/src/Umbraco.Tests/Services/MacroServiceTests.cs +++ b/src/Umbraco.Tests/Services/MacroServiceTests.cs @@ -1,165 +1,165 @@ -using System.Linq; -using NUnit.Framework; -using Umbraco.Core.Models; -using Umbraco.Core.Persistence.Repositories; -using Umbraco.Core.Persistence.UnitOfWork; - -namespace Umbraco.Tests.Services -{ - [TestFixture, RequiresSTA] - public class MacroServiceTests : BaseServiceTest - { - [SetUp] - public override void Initialize() - { - base.Initialize(); - } - - public override void CreateTestData() - { - base.CreateTestData(); - - var provider = new PetaPocoUnitOfWorkProvider(); - using (var unitOfWork = provider.GetUnitOfWork()) - using (var repository = new MacroRepository(unitOfWork)) - { - repository.AddOrUpdate(new Macro("test1", "Test1", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml")); - repository.AddOrUpdate(new Macro("test2", "Test2", "~/usercontrol/test2.ascx", "MyAssembly2", "test2.xslt", "~/views/macropartials/test2.cshtml")); - repository.AddOrUpdate(new Macro("test3", "Tet3", "~/usercontrol/test3.ascx", "MyAssembly3", "test3.xslt", "~/views/macropartials/test3.cshtml")); - unitOfWork.Commit(); - } - } - - [TearDown] - public override void TearDown() - { - base.TearDown(); - } - - [Test] - public void Can_Get_By_Alias() - { - // Arrange - var macroService = ServiceContext.MacroService; - - // Act - var macro = macroService.GetByAlias("test1"); - - //assert - Assert.IsNotNull(macro); - Assert.AreEqual("Test1", macro.Name); - } - - [Test] - public void Can_Get_All() - { - // Arrange - var macroService = ServiceContext.MacroService; - - // Act - var result = macroService.GetAll(); - - //assert - Assert.AreEqual(3, result.Count()); - } - - [Test] - public void Can_Create() - { - // Arrange - var macroService = ServiceContext.MacroService; - - // Act - var macro = new Macro("test", "Test", scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); - macroService.Save(macro); - - //assert - Assert.IsTrue(macro.HasIdentity); - Assert.Greater(macro.Id, 0); - var result = macroService.GetById(macro.Id); - Assert.AreEqual("test", result.Alias); - Assert.AreEqual("Test", result.Name); - Assert.AreEqual("~/Views/MacroPartials/Test.cshtml", result.ScriptPath); - Assert.AreEqual(1234, result.CacheDuration); - } - - [Test] - public void Can_Delete() - { - // Arrange - var macroService = ServiceContext.MacroService; - var macro = new Macro("test", "Test", scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); - macroService.Save(macro); - - // Act - macroService.Delete(macro); - - //assert - var result = macroService.GetById(macro.Id); - Assert.IsNull(result); - } - - [Test] - public void Can_Update() - { - // Arrange - var macroService = ServiceContext.MacroService; - IMacro macro = new Macro("test", "Test", scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); - macroService.Save(macro); - - // Act - macro.Name = "New name"; - macro.Alias = "NewAlias"; - macroService.Save(macro); - - - macro = macroService.GetById(macro.Id); - - //assert - Assert.AreEqual("New name", macro.Name); - Assert.AreEqual("NewAlias", macro.Alias); - - } - - [Test] - public void Can_Update_Property() - { - // Arrange - var macroService = ServiceContext.MacroService; - IMacro macro = new Macro("test", "Test", scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); - macro.Properties.Add(new MacroProperty("blah", "Blah", 0, "blah")); - macroService.Save(macro); - - // Act - macro.Properties.First().Alias = "new Alias"; - macro.Properties.First().Name = "new Name"; - macro.Properties.First().SortOrder = 1; - macro.Properties.First().EditorAlias = "new"; - macroService.Save(macro); - - macro = macroService.GetById(macro.Id); - - //assert - Assert.AreEqual(1, macro.Properties.Count()); - Assert.AreEqual("new Alias", macro.Properties.First().Alias); - Assert.AreEqual("new Name", macro.Properties.First().Name); - Assert.AreEqual(1, macro.Properties.First().SortOrder); - Assert.AreEqual("new", macro.Properties.First().EditorAlias); - - } - - //[Test] - //public void Can_Get_Many_By_Alias() - //{ - // // Arrange - // var macroService = ServiceContext.MacroService; - - // // Act - // var result = macroService.GetAll("test1", "test2"); - - // //assert - // Assert.AreEqual(2, result.Count()); - //} - - } +using System.Linq; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.UnitOfWork; + +namespace Umbraco.Tests.Services +{ + [TestFixture, RequiresSTA] + public class MacroServiceTests : BaseServiceTest + { + [SetUp] + public override void Initialize() + { + base.Initialize(); + } + + public override void CreateTestData() + { + base.CreateTestData(); + + var provider = new PetaPocoUnitOfWorkProvider(); + using (var unitOfWork = provider.GetUnitOfWork()) + using (var repository = new MacroRepository(unitOfWork)) + { + repository.AddOrUpdate(new Macro("test1", "Test1", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml")); + repository.AddOrUpdate(new Macro("test2", "Test2", "~/usercontrol/test2.ascx", "MyAssembly2", "test2.xslt", "~/views/macropartials/test2.cshtml")); + repository.AddOrUpdate(new Macro("test3", "Tet3", "~/usercontrol/test3.ascx", "MyAssembly3", "test3.xslt", "~/views/macropartials/test3.cshtml")); + unitOfWork.Commit(); + } + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); + } + + [Test] + public void Can_Get_By_Alias() + { + // Arrange + var macroService = ServiceContext.MacroService; + + // Act + var macro = macroService.GetByAlias("test1"); + + //assert + Assert.IsNotNull(macro); + Assert.AreEqual("Test1", macro.Name); + } + + [Test] + public void Can_Get_All() + { + // Arrange + var macroService = ServiceContext.MacroService; + + // Act + var result = macroService.GetAll(); + + //assert + Assert.AreEqual(3, result.Count()); + } + + [Test] + public void Can_Create() + { + // Arrange + var macroService = ServiceContext.MacroService; + + // Act + var macro = new Macro("test", "Test", scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); + macroService.Save(macro); + + //assert + Assert.IsTrue(macro.HasIdentity); + Assert.Greater(macro.Id, 0); + var result = macroService.GetById(macro.Id); + Assert.AreEqual("test", result.Alias); + Assert.AreEqual("Test", result.Name); + Assert.AreEqual("~/Views/MacroPartials/Test.cshtml", result.ScriptPath); + Assert.AreEqual(1234, result.CacheDuration); + } + + [Test] + public void Can_Delete() + { + // Arrange + var macroService = ServiceContext.MacroService; + var macro = new Macro("test", "Test", scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); + macroService.Save(macro); + + // Act + macroService.Delete(macro); + + //assert + var result = macroService.GetById(macro.Id); + Assert.IsNull(result); + } + + [Test] + public void Can_Update() + { + // Arrange + var macroService = ServiceContext.MacroService; + IMacro macro = new Macro("test", "Test", scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); + macroService.Save(macro); + + // Act + macro.Name = "New name"; + macro.Alias = "NewAlias"; + macroService.Save(macro); + + + macro = macroService.GetById(macro.Id); + + //assert + Assert.AreEqual("New name", macro.Name); + Assert.AreEqual("NewAlias", macro.Alias); + + } + + [Test] + public void Can_Update_Property() + { + // Arrange + var macroService = ServiceContext.MacroService; + IMacro macro = new Macro("test", "Test", scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); + macro.Properties.Add(new MacroProperty("blah", "Blah", 0, "blah")); + macroService.Save(macro); + + // Act + macro.Properties.First().Alias = "new Alias"; + macro.Properties.First().Name = "new Name"; + macro.Properties.First().SortOrder = 1; + macro.Properties.First().EditorAlias = "new"; + macroService.Save(macro); + + macro = macroService.GetById(macro.Id); + + //assert + Assert.AreEqual(1, macro.Properties.Count()); + Assert.AreEqual("new Alias", macro.Properties.First().Alias); + Assert.AreEqual("new Name", macro.Properties.First().Name); + Assert.AreEqual(1, macro.Properties.First().SortOrder); + Assert.AreEqual("new", macro.Properties.First().EditorAlias); + + } + + //[Test] + //public void Can_Get_Many_By_Alias() + //{ + // // Arrange + // var macroService = ServiceContext.MacroService; + + // // Act + // var result = macroService.GetAll("test1", "test2"); + + // //assert + // Assert.AreEqual(2, result.Count()); + //} + + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedMember.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedMember.cs index 2e019b5ae5..b8088a6793 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedMember.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedMember.cs @@ -1,27 +1,27 @@ -using Umbraco.Core.Models; -using Umbraco.Core.Models.Membership; - -namespace Umbraco.Tests.TestHelpers.Entities -{ - public class MockedMember - { - public static Member CreateSimpleContent(IMemberType contentType, string name, string email, string password, string username, int parentId) - { - var member = new Member(name, email, username, password, parentId, contentType) - { - CreatorId = 0, - Email = email, - Password = password, - Username = username - }; - - member.SetValue("title", name + " member"); - member.SetValue("bodyText", "This is a subpage"); - member.SetValue("author", "John Doe"); - - member.ResetDirtyProperties(false); - - return member; - } - } +using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; + +namespace Umbraco.Tests.TestHelpers.Entities +{ + public class MockedMember + { + public static Member CreateSimpleContent(IMemberType contentType, string name, string email, string password, string username, int parentId) + { + var member = new Member(name, email, username, password, parentId, contentType) + { + CreatorId = 0, + Email = email, + Password = password, + Username = username + }; + + member.SetValue("title", name + " member"); + member.SetValue("bodyText", "This is a subpage"); + member.SetValue("author", "John Doe"); + + member.ResetDirtyProperties(false); + + return member; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/FakeLastChanceFinder.cs b/src/Umbraco.Tests/TestHelpers/Stubs/FakeLastChanceFinder.cs index c9f5dbe024..c2d636c545 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/FakeLastChanceFinder.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/FakeLastChanceFinder.cs @@ -1,12 +1,12 @@ -using Umbraco.Web.Routing; - -namespace Umbraco.Tests.TestHelpers.Stubs -{ - internal class FakeLastChanceFinder : IContentFinder - { - public bool TryFindContent(PublishedContentRequest docRequest) - { - return false; - } - } +using Umbraco.Web.Routing; + +namespace Umbraco.Tests.TestHelpers.Stubs +{ + internal class FakeLastChanceFinder : IContentFinder + { + public bool TryFindContent(PublishedContentRequest docRequest) + { + return false; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs index 5796ee5bc3..6bd000aaf3 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs @@ -1,40 +1,40 @@ -using System; -using System.Linq; -using System.Reflection; -using System.Web.Mvc; -using System.Web.Routing; -using System.Web.SessionState; -using Umbraco.Core; - -namespace Umbraco.Tests.TestHelpers.Stubs -{ - /// - /// Used in place of the UmbracoControllerFactory which relies on BuildManager which throws exceptions in a unit test context - /// - internal class TestControllerFactory : IControllerFactory - { - - public IController CreateController(RequestContext requestContext, string controllerName) - { - var types = TypeFinder.FindClassesOfType(new[] { Assembly.GetExecutingAssembly() }); - - var controllerTypes = types.Where(x => x.Name.Equals(controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase)); - var t = controllerTypes.SingleOrDefault(); - - if (t == null) - return null; - - return Activator.CreateInstance(t) as IController; - } - - public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName) - { - return SessionStateBehavior.Disabled; - } - - public void ReleaseController(IController controller) - { - controller.DisposeIfDisposable(); - } - } +using System; +using System.Linq; +using System.Reflection; +using System.Web.Mvc; +using System.Web.Routing; +using System.Web.SessionState; +using Umbraco.Core; + +namespace Umbraco.Tests.TestHelpers.Stubs +{ + /// + /// Used in place of the UmbracoControllerFactory which relies on BuildManager which throws exceptions in a unit test context + /// + internal class TestControllerFactory : IControllerFactory + { + + public IController CreateController(RequestContext requestContext, string controllerName) + { + var types = TypeFinder.FindClassesOfType(new[] { Assembly.GetExecutingAssembly() }); + + var controllerTypes = types.Where(x => x.Name.Equals(controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase)); + var t = controllerTypes.SingleOrDefault(); + + if (t == null) + return null; + + return Activator.CreateInstance(t) as IController; + } + + public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName) + { + return SessionStateBehavior.Disabled; + } + + public void ReleaseController(IController controller) + { + controller.DisposeIfDisposable(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Trees/BaseContentTreeTests.cs b/src/Umbraco.Tests/Trees/BaseContentTreeTests.cs index 4a2c856a15..63a9f9f14d 100644 --- a/src/Umbraco.Tests/Trees/BaseContentTreeTests.cs +++ b/src/Umbraco.Tests/Trees/BaseContentTreeTests.cs @@ -1,127 +1,127 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using NUnit.Framework; -using umbraco.cms.presentation.Trees; - -namespace Umbraco.Tests.Trees -{ - [TestFixture] - public class BaseContentTreeTests - { - - [TearDown] - public void TestTearDown() - { - BaseTree.AfterTreeRender -= EventHandler; - BaseTree.BeforeTreeRender -= EventHandler; - } - - [Test] - public void Run_Optimized() - { - var tree1 = new MyOptimizedContentTree1("content"); - var tree2 = new MyOptimizedContentTree2("content"); - - Assert.IsTrue(tree1.UseOptimizedRendering); - Assert.IsTrue(tree2.UseOptimizedRendering); - } - - [Test] - public void Not_Optimized_Events_AfterRender() - { - var tree = new MyOptimizedContentTree1("content"); - - BaseTree.AfterTreeRender += EventHandler; - - Assert.IsFalse(tree.UseOptimizedRendering); - } - - [Test] - public void Not_Optimized_Events_BeforeRender() - { - var tree = new MyOptimizedContentTree1("content"); - - BaseTree.BeforeTreeRender += EventHandler; - - Assert.IsFalse(tree.UseOptimizedRendering); - } - - [Test] - public void Not_Optimized_Overriden_Method() - { - var tree = new MyNotOptimizedContentTree("content"); - - Assert.IsFalse(tree.UseOptimizedRendering); - } - - private void EventHandler(object sender, TreeEventArgs treeEventArgs) - { - - } - - //optimized because we are not overriding OnRenderNode - public class MyOptimizedContentTree1 : BaseContentTree - { - public MyOptimizedContentTree1(string application) - : base(application) - { - } - - protected override void CreateRootNode(ref XmlTreeNode rootNode) - { - - } - } - - public class MyOptimizedContentTree2 : BaseContentTree - { - public MyOptimizedContentTree2(string application) - : base(application) - { - } - - protected override bool LoadMinimalDocument - { - get { return true; } - } - - protected override void CreateRootNode(ref XmlTreeNode rootNode) - { - - } - - //even if we override it will still be optimized because of the LoadMinimalDocument flag - protected override void OnRenderNode(ref XmlTreeNode xNode, umbraco.cms.businesslogic.web.Document doc) - { - base.OnRenderNode(ref xNode, doc); - } - } - - public class MyNotOptimizedContentTree : BaseContentTree - { - public MyNotOptimizedContentTree(string application) - : base(application) - { - } - - protected override void CreateRootNode(ref XmlTreeNode rootNode) - { - - } - - protected override bool LoadMinimalDocument - { - get { return false; } - } - - protected override void OnRenderNode(ref XmlTreeNode xNode, umbraco.cms.businesslogic.web.Document doc) - { - base.OnRenderNode(ref xNode, doc); - } - } - - - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using umbraco.cms.presentation.Trees; + +namespace Umbraco.Tests.Trees +{ + [TestFixture] + public class BaseContentTreeTests + { + + [TearDown] + public void TestTearDown() + { + BaseTree.AfterTreeRender -= EventHandler; + BaseTree.BeforeTreeRender -= EventHandler; + } + + [Test] + public void Run_Optimized() + { + var tree1 = new MyOptimizedContentTree1("content"); + var tree2 = new MyOptimizedContentTree2("content"); + + Assert.IsTrue(tree1.UseOptimizedRendering); + Assert.IsTrue(tree2.UseOptimizedRendering); + } + + [Test] + public void Not_Optimized_Events_AfterRender() + { + var tree = new MyOptimizedContentTree1("content"); + + BaseTree.AfterTreeRender += EventHandler; + + Assert.IsFalse(tree.UseOptimizedRendering); + } + + [Test] + public void Not_Optimized_Events_BeforeRender() + { + var tree = new MyOptimizedContentTree1("content"); + + BaseTree.BeforeTreeRender += EventHandler; + + Assert.IsFalse(tree.UseOptimizedRendering); + } + + [Test] + public void Not_Optimized_Overriden_Method() + { + var tree = new MyNotOptimizedContentTree("content"); + + Assert.IsFalse(tree.UseOptimizedRendering); + } + + private void EventHandler(object sender, TreeEventArgs treeEventArgs) + { + + } + + //optimized because we are not overriding OnRenderNode + public class MyOptimizedContentTree1 : BaseContentTree + { + public MyOptimizedContentTree1(string application) + : base(application) + { + } + + protected override void CreateRootNode(ref XmlTreeNode rootNode) + { + + } + } + + public class MyOptimizedContentTree2 : BaseContentTree + { + public MyOptimizedContentTree2(string application) + : base(application) + { + } + + protected override bool LoadMinimalDocument + { + get { return true; } + } + + protected override void CreateRootNode(ref XmlTreeNode rootNode) + { + + } + + //even if we override it will still be optimized because of the LoadMinimalDocument flag + protected override void OnRenderNode(ref XmlTreeNode xNode, umbraco.cms.businesslogic.web.Document doc) + { + base.OnRenderNode(ref xNode, doc); + } + } + + public class MyNotOptimizedContentTree : BaseContentTree + { + public MyNotOptimizedContentTree(string application) + : base(application) + { + } + + protected override void CreateRootNode(ref XmlTreeNode rootNode) + { + + } + + protected override bool LoadMinimalDocument + { + get { return false; } + } + + protected override void OnRenderNode(ref XmlTreeNode xNode, umbraco.cms.businesslogic.web.Document doc) + { + base.OnRenderNode(ref xNode, doc); + } + } + + + } +} diff --git a/src/Umbraco.Tests/Trees/BaseMediaTreeTests.cs b/src/Umbraco.Tests/Trees/BaseMediaTreeTests.cs index 0e93b3db78..5aa0840edf 100644 --- a/src/Umbraco.Tests/Trees/BaseMediaTreeTests.cs +++ b/src/Umbraco.Tests/Trees/BaseMediaTreeTests.cs @@ -1,65 +1,65 @@ -using NUnit.Framework; -using umbraco.cms.presentation.Trees; - -namespace Umbraco.Tests.Trees -{ - [TestFixture] - public class BaseMediaTreeTests - { - - [TearDown] - public void TestTearDown() - { - BaseTree.AfterTreeRender -= EventHandler; - BaseTree.BeforeTreeRender -= EventHandler; - } - - [Test] - public void Run_Optimized() - { - var tree = new MyOptimizedMediaTree("media"); - - Assert.IsTrue(tree.UseOptimizedRendering); - } - - [Test] - public void Not_Optimized_Events_AfterRender() - { - var tree = new MyOptimizedMediaTree("media"); - - BaseTree.AfterTreeRender += EventHandler; - - Assert.IsFalse(tree.UseOptimizedRendering); - } - - [Test] - public void Not_Optimized_Events_BeforeRender() - { - var tree = new MyOptimizedMediaTree("media"); - - BaseTree.BeforeTreeRender += EventHandler; - - Assert.IsFalse(tree.UseOptimizedRendering); - } - - private void EventHandler(object sender, TreeEventArgs treeEventArgs) - { - - } - - public class MyOptimizedMediaTree : BaseMediaTree - { - public MyOptimizedMediaTree(string application) - : base(application) - { - } - - protected override void CreateRootNode(ref XmlTreeNode rootNode) - { - - } - } - - - } +using NUnit.Framework; +using umbraco.cms.presentation.Trees; + +namespace Umbraco.Tests.Trees +{ + [TestFixture] + public class BaseMediaTreeTests + { + + [TearDown] + public void TestTearDown() + { + BaseTree.AfterTreeRender -= EventHandler; + BaseTree.BeforeTreeRender -= EventHandler; + } + + [Test] + public void Run_Optimized() + { + var tree = new MyOptimizedMediaTree("media"); + + Assert.IsTrue(tree.UseOptimizedRendering); + } + + [Test] + public void Not_Optimized_Events_AfterRender() + { + var tree = new MyOptimizedMediaTree("media"); + + BaseTree.AfterTreeRender += EventHandler; + + Assert.IsFalse(tree.UseOptimizedRendering); + } + + [Test] + public void Not_Optimized_Events_BeforeRender() + { + var tree = new MyOptimizedMediaTree("media"); + + BaseTree.BeforeTreeRender += EventHandler; + + Assert.IsFalse(tree.UseOptimizedRendering); + } + + private void EventHandler(object sender, TreeEventArgs treeEventArgs) + { + + } + + public class MyOptimizedMediaTree : BaseMediaTree + { + public MyOptimizedMediaTree(string application) + : base(application) + { + } + + protected override void CreateRootNode(ref XmlTreeNode rootNode) + { + + } + } + + + } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/docs/src/tutorials/Add-ServerSide-Data.ngdoc b/src/Umbraco.Web.UI.Client/docs/src/tutorials/Add-ServerSide-Data.ngdoc index 335cb96ff6..52e4c58443 100644 --- a/src/Umbraco.Web.UI.Client/docs/src/tutorials/Add-ServerSide-Data.ngdoc +++ b/src/Umbraco.Web.UI.Client/docs/src/tutorials/Add-ServerSide-Data.ngdoc @@ -1,157 +1,157 @@ -@ngdoc overview -@name Adding serverside data to a property editor -@description - -##Overview -In this tutorial we will add a serverside API controller, which will query a custom table in the umbraco database, and then return the data to a simple angular controller + view. - -The end result will be a person-list, populated from a custom table, when clicked it will store the ID of the selected person. - -##Setup the database -First thing we need is some data, below is a simple SQL Script for create a `people` table with some random data in. You could also use [http://generatedata.com] for larger amounts of data: - - CREATE TABLE people ( - id INTEGER NOT NULL IDENTITY(1, 1), - name VARCHAR(255) NULL, - town VARCHAR(255) NULL, - country VARCHAR(100) NULL, - PRIMARY KEY (id) - ); - GO - - INSERT INTO people(name,town,country) VALUES('Myles A. Pearson','Tailles','United Kingdom'); - INSERT INTO people(name,town,country) VALUES('Cora Y. Kelly','Froidchapelle','Latvia'); - INSERT INTO people(name,town,country) VALUES('Brooke Baxter','Mogi das Cruzes','Grenada'); - INSERT INTO people(name,town,country) VALUES('Illiana T. Strong','Bevel','Bhutan'); - INSERT INTO people(name,town,country) VALUES('Kaye Frederick','Rothesay','Turkmenistan'); - INSERT INTO people(name,town,country) VALUES('Erasmus Camacho','Sint-Pieters-Kapelle','Saint Vincent and The Grenadines'); - INSERT INTO people(name,town,country) VALUES('Aimee Sampson','Hawera','Antigua and Barbuda'); - - -##Setup ApiController routes -Next we need to defined a `ApiController` to expose a server side route which our application will use to fetch the data. - -For this, we will create a file at: `/app_code/PersonApiController.cs` It must be in app_code since we want our app to compile it on start, alternatively, you can just add it to a normal .net project and compile into a dll as normal. - -In the PersonApiController.cs file, add: - - using System; - using System.Collections.Generic; - using System.Linq; - using System.Web; - - using Umbraco.Web.WebApi; - using Umbraco.Web.Editors; - using Umbraco.Core.Persistence; - - namespace My.Controllers - { - [Umbraco.Web.Mvc.PluginController("My")] - public class PersonApiController : UmbracoAuthorizedJsonController - { - //we will add a method here later - } - } - -This is a very basic Api controller which inherits from `UmbracoAuthorizedJsonController` this specific class will only return json data, and only to requests which are authorized to access the backoffice - -##Setup the GetAll() method -Now that we have a controller, we need to create a method, which can return a collection of people, which our editor will use. - -So first of all, we add a `Person` class to the `My.Controllers` namespace: - - public class Person - { - public int Id { get; set; } - public string Name { get; set; } - public string Town { get; set; } - public string Country { get; set; } - } - -We will use this class to map our table data to an c# class, which we can return as json later. - -Now we need the `GetAll()` method which returns a collection of people, insert this inside the PersonApiController class: - - public IEnumerable GetAll() - { - - } - -Inside the GetAll() method, we now write a bit of code, that connects to the database, creates a query and returns the data, mapped to the `Person` class above: - - //get the database - var db = UmbracoContext.Application.DatabaseContext.Database; - //build a query to select everything the people table - var query = new Sql().Select("*").From("people"); - //fetch data from DB with the query and map to Person object - return db.Fetch(query); - -We are now done with the server side of things, with the file saved in app_code you can now open the Url: /umbraco/My/PersonApi/GetAll - -This will return our json code. - -##Create a Person Resource -Now that we have the serverside in place, and a Url to call, we will setup a service to retrieve our data. As an Umbraco specific convention, we call these services a *resource, so we always have an indication what services fetch data from the DB. - -Create a new file as `person.resource.js` and add: - - //adds the resource to umbraco.resources module: - angular.module('umbraco.resources').factory('personResource', - function($q, $http) { - //the factory object returned - return { - //this cals the Api Controller we setup earlier - getAll: function () { - return $http.get("My/PersonApi/GetAll"); - } - }; - } - ); - -This uses the standard angular factory pattern, so we can now inject this into any of our controllers under the name `personResource`. - -the getAll method just returns a $http.get call, which handles calling the url, and will return the data when its ready. - -##Create the view and controller -We will now finally setup a new view and controller, which follows previous tutorials, so have refer to those for more details: - -####the view: - -
- -
- -####The controller: - - angular.module("umbraco") - .controller("My.PersonPickerController", function($scope, personResource){ - personResource.getAll().then(function(response){ - $scope.people = response.data; - }); - }); - -##The flow -So with all these bits in place, all you need to do is register the property editor in a package.manifest - have a look at the first tutorial in this series. You will need to tell the package to load both your personpicker.controller.js and the person.resource.js file on app start. - -With this, the entire flow is: - -1. the view renders a list of people with a controller -2. the controller asks the personResource for data -3. the personResource returns a promise and asks the /my/PersonAPI api controller -4. The apicontroller queries the database, which returns the data as strongly typed Person objects -5. the api controller returns those `Person` objects as json to the resource -6. the resource resolve the promise -7. the controller populates the view - -Easy huh? - honestly tho, there is a good amount of things to keep track of, but each component is tiny and flexible. - -##Wrap-up -The important part of the above is the way you create an `ApiController` call the database for your own data, and finally expose the data to angular as a service using $http. - -For simplicity, you could also have skipped the service part, and just called $http directly in your controller, but by having your data in a service, it becomes a reusable resource for your entire application. - - +@ngdoc overview +@name Adding serverside data to a property editor +@description + +##Overview +In this tutorial we will add a serverside API controller, which will query a custom table in the umbraco database, and then return the data to a simple angular controller + view. + +The end result will be a person-list, populated from a custom table, when clicked it will store the ID of the selected person. + +##Setup the database +First thing we need is some data, below is a simple SQL Script for create a `people` table with some random data in. You could also use [http://generatedata.com] for larger amounts of data: + + CREATE TABLE people ( + id INTEGER NOT NULL IDENTITY(1, 1), + name VARCHAR(255) NULL, + town VARCHAR(255) NULL, + country VARCHAR(100) NULL, + PRIMARY KEY (id) + ); + GO + + INSERT INTO people(name,town,country) VALUES('Myles A. Pearson','Tailles','United Kingdom'); + INSERT INTO people(name,town,country) VALUES('Cora Y. Kelly','Froidchapelle','Latvia'); + INSERT INTO people(name,town,country) VALUES('Brooke Baxter','Mogi das Cruzes','Grenada'); + INSERT INTO people(name,town,country) VALUES('Illiana T. Strong','Bevel','Bhutan'); + INSERT INTO people(name,town,country) VALUES('Kaye Frederick','Rothesay','Turkmenistan'); + INSERT INTO people(name,town,country) VALUES('Erasmus Camacho','Sint-Pieters-Kapelle','Saint Vincent and The Grenadines'); + INSERT INTO people(name,town,country) VALUES('Aimee Sampson','Hawera','Antigua and Barbuda'); + + +##Setup ApiController routes +Next we need to defined a `ApiController` to expose a server side route which our application will use to fetch the data. + +For this, we will create a file at: `/app_code/PersonApiController.cs` It must be in app_code since we want our app to compile it on start, alternatively, you can just add it to a normal .net project and compile into a dll as normal. + +In the PersonApiController.cs file, add: + + using System; + using System.Collections.Generic; + using System.Linq; + using System.Web; + + using Umbraco.Web.WebApi; + using Umbraco.Web.Editors; + using Umbraco.Core.Persistence; + + namespace My.Controllers + { + [Umbraco.Web.Mvc.PluginController("My")] + public class PersonApiController : UmbracoAuthorizedJsonController + { + //we will add a method here later + } + } + +This is a very basic Api controller which inherits from `UmbracoAuthorizedJsonController` this specific class will only return json data, and only to requests which are authorized to access the backoffice + +##Setup the GetAll() method +Now that we have a controller, we need to create a method, which can return a collection of people, which our editor will use. + +So first of all, we add a `Person` class to the `My.Controllers` namespace: + + public class Person + { + public int Id { get; set; } + public string Name { get; set; } + public string Town { get; set; } + public string Country { get; set; } + } + +We will use this class to map our table data to an c# class, which we can return as json later. + +Now we need the `GetAll()` method which returns a collection of people, insert this inside the PersonApiController class: + + public IEnumerable GetAll() + { + + } + +Inside the GetAll() method, we now write a bit of code, that connects to the database, creates a query and returns the data, mapped to the `Person` class above: + + //get the database + var db = UmbracoContext.Application.DatabaseContext.Database; + //build a query to select everything the people table + var query = new Sql().Select("*").From("people"); + //fetch data from DB with the query and map to Person object + return db.Fetch(query); + +We are now done with the server side of things, with the file saved in app_code you can now open the Url: /umbraco/My/PersonApi/GetAll + +This will return our json code. + +##Create a Person Resource +Now that we have the serverside in place, and a Url to call, we will setup a service to retrieve our data. As an Umbraco specific convention, we call these services a *resource, so we always have an indication what services fetch data from the DB. + +Create a new file as `person.resource.js` and add: + + //adds the resource to umbraco.resources module: + angular.module('umbraco.resources').factory('personResource', + function($q, $http) { + //the factory object returned + return { + //this cals the Api Controller we setup earlier + getAll: function () { + return $http.get("My/PersonApi/GetAll"); + } + }; + } + ); + +This uses the standard angular factory pattern, so we can now inject this into any of our controllers under the name `personResource`. + +the getAll method just returns a $http.get call, which handles calling the url, and will return the data when its ready. + +##Create the view and controller +We will now finally setup a new view and controller, which follows previous tutorials, so have refer to those for more details: + +####the view: + +
+ +
+ +####The controller: + + angular.module("umbraco") + .controller("My.PersonPickerController", function($scope, personResource){ + personResource.getAll().then(function(response){ + $scope.people = response.data; + }); + }); + +##The flow +So with all these bits in place, all you need to do is register the property editor in a package.manifest - have a look at the first tutorial in this series. You will need to tell the package to load both your personpicker.controller.js and the person.resource.js file on app start. + +With this, the entire flow is: + +1. the view renders a list of people with a controller +2. the controller asks the personResource for data +3. the personResource returns a promise and asks the /my/PersonAPI api controller +4. The apicontroller queries the database, which returns the data as strongly typed Person objects +5. the api controller returns those `Person` objects as json to the resource +6. the resource resolve the promise +7. the controller populates the view + +Easy huh? - honestly tho, there is a good amount of things to keep track of, but each component is tiny and flexible. + +##Wrap-up +The important part of the above is the way you create an `ApiController` call the database for your own data, and finally expose the data to angular as a service using $http. + +For simplicity, you could also have skipped the service part, and just called $http directly in your controller, but by having your data in a service, it becomes a reusable resource for your entire application. + + diff --git a/src/Umbraco.Web.UI.Client/docs/src/tutorials/Creating-Editors-Trees.ngdoc b/src/Umbraco.Web.UI.Client/docs/src/tutorials/Creating-Editors-Trees.ngdoc index 50596e4c7f..06066eb58b 100644 --- a/src/Umbraco.Web.UI.Client/docs/src/tutorials/Creating-Editors-Trees.ngdoc +++ b/src/Umbraco.Web.UI.Client/docs/src/tutorials/Creating-Editors-Trees.ngdoc @@ -1,165 +1,165 @@ -@ngdoc overview -@name Creating a custom tree with an editor and dialog -@description - -##Overview - -This guide will explain how to create a simple custom tree an angular editor & dialog using standard conventions. This guide does not go into detail about how to persist data in your editors, it is a simple tutorial defining how routing interacts with your views and where your views need to be stored. - -So all the steps we will go through: - -- Creating a tree with a menu item -- Create an editor -- Create a dialog for the menu item - -##Create a tree - -First you need to define a tree class that inherits from `Umbraco.Web.Trees.TreeController` - - public class MyCustomTreeController : TreeController - { - } - -The name of your tree must be suffixed with the term 'Controller'. - -Next we need to add some attributes to the tree. The first one defines the section it belongs to, the tree alias and it's name. Ensure your tree alias is unique, tree aliases cannot overlap. - - [Tree("settings", "myTree", "My Tree")] - -The 2nd attribute does 2 things - Tells Umbraco how to route the tree and tells Umbraco where to find the view files. This attribute is not required but if you do not specify it then the view location conventions will not work. - - [PluginController("MyPackage")] - -There are 2 methods that need to be overriden from the TreeController class: `GetTreeNodes` & `GetMenuForNode`. This example will create 3 nodes underneath the root node: - - protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) - { - //check if we're rendering the root node's children - if (id == Constants.System.Root.ToInvariantString()) - { - var tree = new TreeNodeCollection - { - CreateTreeNode("1", queryStrings, "My Node 1"), - CreateTreeNode("2", queryStrings, "My Node 2"), - CreateTreeNode("3", queryStrings, "My Node 3") - }; - return tree; - } - //this tree doesn't suport rendering more than 1 level - throw new NotSupportedException(); - } - -Next we'll create a menu item for each node, in this case its a 'Create' menu item - - protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) - { - var menu = new MenuItemCollection(); - menu.AddMenuItem(new MenuItem("create", "Create")); - return menu; - } - -That's it, the whole tree looks like this: - - [Tree("settings", "myTree", "My Tree")] - [PluginController("MyPackage")] - public class MyCustomTreeController : TreeController - { - protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) - { - //check if we're rendering the root node's children - if (id == Constants.System.Root.ToInvariantString()) - { - var tree = new TreeNodeCollection - { - CreateTreeNode("1", queryStrings, "My Node 1"), - CreateTreeNode("2", queryStrings, "My Node 2"), - CreateTreeNode("3", queryStrings, "My Node 3") - }; - return tree; - } - //this tree doesn't suport rendering more than 1 level - throw new NotSupportedException(); - } - protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) - { - var menu = new MenuItemCollection(); - menu.AddMenuItem(new MenuItem("create", "Create")); - return menu; - } - } - -##View path conventions - -Now that we've created our tree it is important to understand Umbraco conventions and where it will look for your views for editors and dialogs. - -###Angular editor routes - -The main angular route to load in editors is */:section/:tree/:method/:id* - -Umbraco will load in views for this route based on these conventions: - - * If it is a core tree - views will be loaded from: */umbraco/views/{treetype}/{method}.html* - * If it is a custom (package) tree - views will be loaded from: */App_Plugins/{mypackage}/BackOffice/{treetype}/{method}.html* - -###Editor locations - -By default each tree node's 'method' is assigned as 'edit' therefore these are the view paths for an editor when a tree node is clicked: - - * If it is a core tree - views will be loaded from: */umbraco/views/{treetype}/edit.html* - * If it is a custom (package) tree - views will be loaded from: */App_Plugins/{mypackage}/BackOffice/{treetype}/edit.html* - -Developers can specify a custom `RoutePath` for any tree node which will cause umbraco to route to that specific location. - -###Dialog locations - -Dialog view path locations are similar to editors: - - * If it is a core tree - views will be loaded from: umbraco/views/{treetype}/{action}.html - * If it is a custom (package) tree - views will be loaded from: /App_Plugins/{mypackage}/BackOffice/{treetype}/{action}.html - -'action' is the alias of your menu item, for example in the menu item in the example above this would be 'create'. - -##Create an editor - -An editor is simply an angular view (html file) so you can really do whatever you'd like! This tutorial will simply create a hello world editor showing the id of the item being edited. - -###Create a controller - -First thing we'll do is create an angular controller for the editor, this controller will be contained in a file found beside the view - *the file naming conventions are based on the controller file naming conventions in the Umbraco core*. - -/App_Plugins/MyPackage/BackOffice/MyTree/mypackage.mytree.edit.controller.js - -The controller is super simple, at it is going to do is assign a property to the $scope which shows the current item id being edited: - - 'use strict'; - (function () { - //create the controller - function myTreeEditController($scope, $routeParams) { - //set a property on the scope equal to the current route id - $scope.id = $routeParams.id; - }; - //register the controller - angular.module("umbraco").controller('MyPackage.MyTree.EditController', myTreeEditController); - })(); - -###Create a view - -As per the conventions above our editor view will need to be located at: - -/App_Plugins/MyPackage/BackOffice/MyTree/edit.html - -The view is simple, it is just going to show the current id being edited - -
-

Hello world!

-

- You are current editing an item with id {{id}} -

-
- -##Create a dialog - -This is the same principle as an editor, you just need to follow conventions. Based on the above conventions the 'create' dialog view will be located here: - -/App_Plugins/MyPackage/BackOffice/MyTree/create.html - +@ngdoc overview +@name Creating a custom tree with an editor and dialog +@description + +##Overview + +This guide will explain how to create a simple custom tree an angular editor & dialog using standard conventions. This guide does not go into detail about how to persist data in your editors, it is a simple tutorial defining how routing interacts with your views and where your views need to be stored. + +So all the steps we will go through: + +- Creating a tree with a menu item +- Create an editor +- Create a dialog for the menu item + +##Create a tree + +First you need to define a tree class that inherits from `Umbraco.Web.Trees.TreeController` + + public class MyCustomTreeController : TreeController + { + } + +The name of your tree must be suffixed with the term 'Controller'. + +Next we need to add some attributes to the tree. The first one defines the section it belongs to, the tree alias and it's name. Ensure your tree alias is unique, tree aliases cannot overlap. + + [Tree("settings", "myTree", "My Tree")] + +The 2nd attribute does 2 things - Tells Umbraco how to route the tree and tells Umbraco where to find the view files. This attribute is not required but if you do not specify it then the view location conventions will not work. + + [PluginController("MyPackage")] + +There are 2 methods that need to be overriden from the TreeController class: `GetTreeNodes` & `GetMenuForNode`. This example will create 3 nodes underneath the root node: + + protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) + { + //check if we're rendering the root node's children + if (id == Constants.System.Root.ToInvariantString()) + { + var tree = new TreeNodeCollection + { + CreateTreeNode("1", queryStrings, "My Node 1"), + CreateTreeNode("2", queryStrings, "My Node 2"), + CreateTreeNode("3", queryStrings, "My Node 3") + }; + return tree; + } + //this tree doesn't suport rendering more than 1 level + throw new NotSupportedException(); + } + +Next we'll create a menu item for each node, in this case its a 'Create' menu item + + protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) + { + var menu = new MenuItemCollection(); + menu.AddMenuItem(new MenuItem("create", "Create")); + return menu; + } + +That's it, the whole tree looks like this: + + [Tree("settings", "myTree", "My Tree")] + [PluginController("MyPackage")] + public class MyCustomTreeController : TreeController + { + protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) + { + //check if we're rendering the root node's children + if (id == Constants.System.Root.ToInvariantString()) + { + var tree = new TreeNodeCollection + { + CreateTreeNode("1", queryStrings, "My Node 1"), + CreateTreeNode("2", queryStrings, "My Node 2"), + CreateTreeNode("3", queryStrings, "My Node 3") + }; + return tree; + } + //this tree doesn't suport rendering more than 1 level + throw new NotSupportedException(); + } + protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) + { + var menu = new MenuItemCollection(); + menu.AddMenuItem(new MenuItem("create", "Create")); + return menu; + } + } + +##View path conventions + +Now that we've created our tree it is important to understand Umbraco conventions and where it will look for your views for editors and dialogs. + +###Angular editor routes + +The main angular route to load in editors is */:section/:tree/:method/:id* + +Umbraco will load in views for this route based on these conventions: + + * If it is a core tree - views will be loaded from: */umbraco/views/{treetype}/{method}.html* + * If it is a custom (package) tree - views will be loaded from: */App_Plugins/{mypackage}/BackOffice/{treetype}/{method}.html* + +###Editor locations + +By default each tree node's 'method' is assigned as 'edit' therefore these are the view paths for an editor when a tree node is clicked: + + * If it is a core tree - views will be loaded from: */umbraco/views/{treetype}/edit.html* + * If it is a custom (package) tree - views will be loaded from: */App_Plugins/{mypackage}/BackOffice/{treetype}/edit.html* + +Developers can specify a custom `RoutePath` for any tree node which will cause umbraco to route to that specific location. + +###Dialog locations + +Dialog view path locations are similar to editors: + + * If it is a core tree - views will be loaded from: umbraco/views/{treetype}/{action}.html + * If it is a custom (package) tree - views will be loaded from: /App_Plugins/{mypackage}/BackOffice/{treetype}/{action}.html + +'action' is the alias of your menu item, for example in the menu item in the example above this would be 'create'. + +##Create an editor + +An editor is simply an angular view (html file) so you can really do whatever you'd like! This tutorial will simply create a hello world editor showing the id of the item being edited. + +###Create a controller + +First thing we'll do is create an angular controller for the editor, this controller will be contained in a file found beside the view - *the file naming conventions are based on the controller file naming conventions in the Umbraco core*. + +/App_Plugins/MyPackage/BackOffice/MyTree/mypackage.mytree.edit.controller.js + +The controller is super simple, at it is going to do is assign a property to the $scope which shows the current item id being edited: + + 'use strict'; + (function () { + //create the controller + function myTreeEditController($scope, $routeParams) { + //set a property on the scope equal to the current route id + $scope.id = $routeParams.id; + }; + //register the controller + angular.module("umbraco").controller('MyPackage.MyTree.EditController', myTreeEditController); + })(); + +###Create a view + +As per the conventions above our editor view will need to be located at: + +/App_Plugins/MyPackage/BackOffice/MyTree/edit.html + +The view is simple, it is just going to show the current id being edited + +
+

Hello world!

+

+ You are current editing an item with id {{id}} +

+
+ +##Create a dialog + +This is the same principle as an editor, you just need to follow conventions. Based on the above conventions the 'create' dialog view will be located here: + +/App_Plugins/MyPackage/BackOffice/MyTree/create.html + diff --git a/src/Umbraco.Web.UI.Client/docs/src/tutorials/Tag-Enabled-Property-Editors.ngdoc b/src/Umbraco.Web.UI.Client/docs/src/tutorials/Tag-Enabled-Property-Editors.ngdoc index ef1fe84f00..ddcdba0e73 100644 --- a/src/Umbraco.Web.UI.Client/docs/src/tutorials/Tag-Enabled-Property-Editors.ngdoc +++ b/src/Umbraco.Web.UI.Client/docs/src/tutorials/Tag-Enabled-Property-Editors.ngdoc @@ -1,52 +1,52 @@ -@ngdoc overview -@name Adding tag support to property editors -@description - -##What is tag support? - -This document will explain how to add tagging support to property editors and what tagging support means. A property editor that has tag support enabled means -that any time a document is published (or saved in the case of media/members) that has a property editor with tagging support, the tags the are exposed -by these property editors will be saved into the tag table in the database and associated with the corresponding document property. This then allows you -to query or render content based on tags on the front-end. - -*NOTE: The documentation for querying tag data is coming soon....* - -##Enabling tag support - -This will explain both ways to enable tag support for property editors: both in c# or by the manifest. - -### CSharp - -Like creating a normal c# property editor you start by declaring a class with the PropertyEditor attribute: - - [PropertyEditor("tags", "Tags", "tags")] - public class TagsPropertyEditor : PropertyEditor - { - } - -To add tag support, we simply add the SupportsTags attribute: - - [SupportTags] - [PropertyEditor(Constants.PropertyEditors.TagsAlias, "Tags", "readonlyvalue")] - public class TagsPropertyEditor : PropertyEditor - { - } - -There are a few options for the `SupportsTag` attribute but normally the default options will suffice. -The default options are: - -* Requires that the property editor value is a comma delimited string value -* Will replace the current property's tags in the tags database table with the ones in the value -* Will add all tags as part of the 'default' tag group in the database table - -These options can all be changed on the attribute, you can specify a different delimiter, you can specify whether to replace or append the tags associated with the property and you can specify a different tag group. - -There is one last option that can be set which is the `TagValueType` enum, the values can be: - -* `FromDelimitedValue` - this is the default -* `CustomTagList` - if this is used then it is expected that the property editor's value (returned from the method `ConvertEditorToDb`) is an `IEnumerable` - * This setting might be handy if you need to dynamically (in c#) generate the tags list - -### Manifest - +@ngdoc overview +@name Adding tag support to property editors +@description + +##What is tag support? + +This document will explain how to add tagging support to property editors and what tagging support means. A property editor that has tag support enabled means +that any time a document is published (or saved in the case of media/members) that has a property editor with tagging support, the tags the are exposed +by these property editors will be saved into the tag table in the database and associated with the corresponding document property. This then allows you +to query or render content based on tags on the front-end. + +*NOTE: The documentation for querying tag data is coming soon....* + +##Enabling tag support + +This will explain both ways to enable tag support for property editors: both in c# or by the manifest. + +### CSharp + +Like creating a normal c# property editor you start by declaring a class with the PropertyEditor attribute: + + [PropertyEditor("tags", "Tags", "tags")] + public class TagsPropertyEditor : PropertyEditor + { + } + +To add tag support, we simply add the SupportsTags attribute: + + [SupportTags] + [PropertyEditor(Constants.PropertyEditors.TagsAlias, "Tags", "readonlyvalue")] + public class TagsPropertyEditor : PropertyEditor + { + } + +There are a few options for the `SupportsTag` attribute but normally the default options will suffice. +The default options are: + +* Requires that the property editor value is a comma delimited string value +* Will replace the current property's tags in the tags database table with the ones in the value +* Will add all tags as part of the 'default' tag group in the database table + +These options can all be changed on the attribute, you can specify a different delimiter, you can specify whether to replace or append the tags associated with the property and you can specify a different tag group. + +There is one last option that can be set which is the `TagValueType` enum, the values can be: + +* `FromDelimitedValue` - this is the default +* `CustomTagList` - if this is used then it is expected that the property editor's value (returned from the method `ConvertEditorToDb`) is an `IEnumerable` + * This setting might be handy if you need to dynamically (in c#) generate the tags list + +### Manifest + *Coming soon!* \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/jquery/jquery.sortable/jquery.sortable.min.js b/src/Umbraco.Web.UI.Client/lib/jquery/jquery.sortable/jquery.sortable.min.js index a08edb37bd..dbdd163912 100644 --- a/src/Umbraco.Web.UI.Client/lib/jquery/jquery.sortable/jquery.sortable.min.js +++ b/src/Umbraco.Web.UI.Client/lib/jquery/jquery.sortable/jquery.sortable.min.js @@ -1,17 +1,17 @@ -!function(e,x,h){function r(a,b){var c=Math.max(0,a[0]-b[0],b[0]-a[1]),d=Math.max(0,a[2]-b[1],b[1]-a[3]);return c+d}function s(a,b,c,d){for(var f=a.length,d=d?"offset":"position",c=c||0;f--;){var k=a[f].el?a[f].el:e(a[f]),i=k[d]();i.left+=parseInt(k.css("margin-left"),10);i.top+=parseInt(k.css("margin-top"),10);b[f]=[i.left-c,i.left+k.outerWidth()+c,i.top-c,i.top+k.outerHeight()+c]}}function l(a,b){var c=b.offset();return{left:a.left-c.left,top:a.top-c.top}}function t(a,b,c){for(var b=[b.left,b.top], -c=c&&[c.left,c.top],d,f=a.length,e=[];f--;)d=a[f],e[f]=[f,r(d,b),c&&r(d,c)];return e=e.sort(function(a,b){return b[1]-a[1]||b[2]-a[2]||b[0]-a[0]})}function m(a){this.options=e.extend({},j,a);this.containers=[];this.scrollProxy=e.proxy(this.scroll,this);this.dragProxy=e.proxy(this.drag,this);this.dropProxy=e.proxy(this.drop,this);this.options.parentContainer||(this.placeholder=e(this.options.placeholder),a.isValidTarget||(this.options.isValidTarget=h))}function n(a,b){this.el=a;this.options=e.extend({}, -v,b);this.group=m.get(this.options);this.rootGroup=this.options.rootGroup=this.options.rootGroup||this.group;this.parentContainer=this.options.parentContainer;this.handle=this.rootGroup.options.handle||this.rootGroup.options.itemSelector;this.el.on(o.start,this.handle,e.proxy(this.dragInit,this));this.options.drop&&this.group.containers.push(this)}var o,v={drag:!0,drop:!0,exclude:"",nested:!0,vertical:!0},j={afterMove:function(){},containerPath:"",containerSelector:"ol, ul",distance:0,handle:"",itemPath:"", -itemSelector:"li",isValidTarget:function(){return!0},onCancel:function(){},onDrag:function(a,b){a.css(b)},onDragStart:function(a){a.css({height:a.height(),width:a.width()});a.addClass("dragged");e("body").addClass("dragging")},onDrop:function(a){a.removeClass("dragged").removeAttr("style");e("body").removeClass("dragging")},onMousedown:function(a,b){b.preventDefault()},placeholder:'
  • ',pullPlaceholder:!0,serialize:function(a,b,c){a=e.extend({},a.data());if(c)return b;b[0]&& -(a.children=b,delete a.subContainer);delete a.sortable;return a},tolerance:0},p={},u=0,w={left:0,top:0,bottom:0,right:0};o={start:"touchstart.sortable mousedown.sortable",drop:"touchend.sortable touchcancel.sortable mouseup.sortable",drag:"touchmove.sortable mousemove.sortable",scroll:"scroll.sortable"};m.get=function(a){p[a.group]||(a.group||(a.group=u++),p[a.group]=new m(a));return p[a.group]};m.prototype={dragInit:function(a,b){this.$document=e(b.el[0].ownerDocument);b.enabled()?(this.toggleListeners("on"), -this.item=e(a.target).closest(this.options.itemSelector),this.itemContainer=b,this.setPointer(a),this.options.onMousedown(this.item,a,j.onMousedown)):this.toggleListeners("on",["drop"]);this.dragInitDone=!0},drag:function(a){if(!this.dragging){if(!this.distanceMet(a))return;this.options.onDragStart(this.item,this.itemContainer,j.onDragStart);this.item.before(this.placeholder);this.dragging=!0}this.setPointer(a);this.options.onDrag(this.item,l(this.pointer,this.item.offsetParent()),j.onDrag);var b= -a.pageX,a=a.pageY,c=this.sameResultBox,d=this.options.tolerance;if(!c||c.top-d>a||c.bottom+db||c.right+d=this.options.distance},scroll:function(){this.clearDimensions();this.clearOffsetParent()},toggleListeners:function(a,b){var c=this,b=b||["drag","drop","scroll"];e.each(b,function(b,f){c.$document[a](o[f],c[f+"Proxy"])})},clearOffsetParent:function(){this.offsetParent=h},clearDimensions:function(){this.containerDimensions=h;for(var a=this.containers.length;a--;)this.containers[a].clearDimensions()}};n.prototype={dragInit:function(a){var b=this.rootGroup; -!b.dragInitDone&&1===a.which&&this.options.drag&&!e(a.target).is(this.options.exclude)&&b.dragInit(a,this)},searchValidTarget:function(a,b){var c=t(this.getItemDimensions(),a,b),d=c.length,f=this.rootGroup,e=!f.options.isValidTarget||f.options.isValidTarget(f.item,this);if(!d&&e)return f.movePlaceholder(this,this.el,"append"),!0;for(;d--;)if(f=c[d][0],!c[d][1]&&this.hasChildGroup(f)){if(this.getContainerGroup(f).searchValidTarget(a,b))return!0}else if(e)return this.movePlaceholder(f,a),!0},movePlaceholder:function(a, -b){var c=e(this.items[a]),d=this.itemDimensions[a],f="after",h=c.outerWidth(),i=c.outerHeight(),g=c.offset(),g={left:g.left,right:g.left+h,top:g.top,bottom:g.top+i};this.options.vertical?b.top<=(d[2]+d[3])/2?(f="before",g.bottom-=i/2):g.top+=i/2:b.left<=(d[0]+d[1])/2?(f="before",g.right-=h/2):g.left+=h/2;this.hasChildGroup(a)&&(g=w);this.rootGroup.movePlaceholder(this,c,f,g)},getItemDimensions:function(){this.itemDimensions||(this.items=this.$getChildren(this.el,"item").filter(":not(.placeholder, .dragged)").get(), -s(this.items,this.itemDimensions=[],this.options.tolerance));return this.itemDimensions},getItemOffsetParent:function(){var a=this.el;return"relative"===a.css("position")||"absolute"===a.css("position")||"fixed"===a.css("position")?a:a.offsetParent()},hasChildGroup:function(a){return this.options.nested&&this.getContainerGroup(a)},getContainerGroup:function(a){var b=e.data(this.items[a],"subContainer");if(b===h){var c=this.$getChildren(this.items[a],"container"),b=!1;c[0]&&(b=e.extend({},this.options, -{parentContainer:this,group:u++}),b=c.sortable(b).data("sortable").group);e.data(this.items[a],"subContainer",b)}return b},enabled:function(){return!this.disabled&&(!this.parentContainer||this.parentContainer.enabled())},$getChildren:function(a,b){var c=this.rootGroup.options,d=c[b+"Path"],c=c[b+"Selector"],a=e(a);d&&(a=a.find(d));return a.children(c)},_serialize:function(a,b){var c=this,d=this.$getChildren(a,b?"item":"container").not(this.options.exclude).map(function(){return c._serialize(e(this), -!b)}).get();return this.rootGroup.options.serialize(a,d,b)},clearDimensions:function(){this.itemDimensions=h;if(this.items&&this.items[0])for(var a=this.items.length;a--;){var b=e.data(this.items[a],"subContainer");b&&b.clearDimensions()}}};var q={enable:function(){this.disabled=!1},disable:function(){this.disabled=!0},serialize:function(){return this._serialize(this.el,!0)}};e.extend(n.prototype,q);e.fn.sortable=function(a){var b=Array.prototype.slice.call(arguments,1);return this.map(function(){var c= +!function(e,x,h){function r(a,b){var c=Math.max(0,a[0]-b[0],b[0]-a[1]),d=Math.max(0,a[2]-b[1],b[1]-a[3]);return c+d}function s(a,b,c,d){for(var f=a.length,d=d?"offset":"position",c=c||0;f--;){var k=a[f].el?a[f].el:e(a[f]),i=k[d]();i.left+=parseInt(k.css("margin-left"),10);i.top+=parseInt(k.css("margin-top"),10);b[f]=[i.left-c,i.left+k.outerWidth()+c,i.top-c,i.top+k.outerHeight()+c]}}function l(a,b){var c=b.offset();return{left:a.left-c.left,top:a.top-c.top}}function t(a,b,c){for(var b=[b.left,b.top], +c=c&&[c.left,c.top],d,f=a.length,e=[];f--;)d=a[f],e[f]=[f,r(d,b),c&&r(d,c)];return e=e.sort(function(a,b){return b[1]-a[1]||b[2]-a[2]||b[0]-a[0]})}function m(a){this.options=e.extend({},j,a);this.containers=[];this.scrollProxy=e.proxy(this.scroll,this);this.dragProxy=e.proxy(this.drag,this);this.dropProxy=e.proxy(this.drop,this);this.options.parentContainer||(this.placeholder=e(this.options.placeholder),a.isValidTarget||(this.options.isValidTarget=h))}function n(a,b){this.el=a;this.options=e.extend({}, +v,b);this.group=m.get(this.options);this.rootGroup=this.options.rootGroup=this.options.rootGroup||this.group;this.parentContainer=this.options.parentContainer;this.handle=this.rootGroup.options.handle||this.rootGroup.options.itemSelector;this.el.on(o.start,this.handle,e.proxy(this.dragInit,this));this.options.drop&&this.group.containers.push(this)}var o,v={drag:!0,drop:!0,exclude:"",nested:!0,vertical:!0},j={afterMove:function(){},containerPath:"",containerSelector:"ol, ul",distance:0,handle:"",itemPath:"", +itemSelector:"li",isValidTarget:function(){return!0},onCancel:function(){},onDrag:function(a,b){a.css(b)},onDragStart:function(a){a.css({height:a.height(),width:a.width()});a.addClass("dragged");e("body").addClass("dragging")},onDrop:function(a){a.removeClass("dragged").removeAttr("style");e("body").removeClass("dragging")},onMousedown:function(a,b){b.preventDefault()},placeholder:'
  • ',pullPlaceholder:!0,serialize:function(a,b,c){a=e.extend({},a.data());if(c)return b;b[0]&& +(a.children=b,delete a.subContainer);delete a.sortable;return a},tolerance:0},p={},u=0,w={left:0,top:0,bottom:0,right:0};o={start:"touchstart.sortable mousedown.sortable",drop:"touchend.sortable touchcancel.sortable mouseup.sortable",drag:"touchmove.sortable mousemove.sortable",scroll:"scroll.sortable"};m.get=function(a){p[a.group]||(a.group||(a.group=u++),p[a.group]=new m(a));return p[a.group]};m.prototype={dragInit:function(a,b){this.$document=e(b.el[0].ownerDocument);b.enabled()?(this.toggleListeners("on"), +this.item=e(a.target).closest(this.options.itemSelector),this.itemContainer=b,this.setPointer(a),this.options.onMousedown(this.item,a,j.onMousedown)):this.toggleListeners("on",["drop"]);this.dragInitDone=!0},drag:function(a){if(!this.dragging){if(!this.distanceMet(a))return;this.options.onDragStart(this.item,this.itemContainer,j.onDragStart);this.item.before(this.placeholder);this.dragging=!0}this.setPointer(a);this.options.onDrag(this.item,l(this.pointer,this.item.offsetParent()),j.onDrag);var b= +a.pageX,a=a.pageY,c=this.sameResultBox,d=this.options.tolerance;if(!c||c.top-d>a||c.bottom+db||c.right+d=this.options.distance},scroll:function(){this.clearDimensions();this.clearOffsetParent()},toggleListeners:function(a,b){var c=this,b=b||["drag","drop","scroll"];e.each(b,function(b,f){c.$document[a](o[f],c[f+"Proxy"])})},clearOffsetParent:function(){this.offsetParent=h},clearDimensions:function(){this.containerDimensions=h;for(var a=this.containers.length;a--;)this.containers[a].clearDimensions()}};n.prototype={dragInit:function(a){var b=this.rootGroup; +!b.dragInitDone&&1===a.which&&this.options.drag&&!e(a.target).is(this.options.exclude)&&b.dragInit(a,this)},searchValidTarget:function(a,b){var c=t(this.getItemDimensions(),a,b),d=c.length,f=this.rootGroup,e=!f.options.isValidTarget||f.options.isValidTarget(f.item,this);if(!d&&e)return f.movePlaceholder(this,this.el,"append"),!0;for(;d--;)if(f=c[d][0],!c[d][1]&&this.hasChildGroup(f)){if(this.getContainerGroup(f).searchValidTarget(a,b))return!0}else if(e)return this.movePlaceholder(f,a),!0},movePlaceholder:function(a, +b){var c=e(this.items[a]),d=this.itemDimensions[a],f="after",h=c.outerWidth(),i=c.outerHeight(),g=c.offset(),g={left:g.left,right:g.left+h,top:g.top,bottom:g.top+i};this.options.vertical?b.top<=(d[2]+d[3])/2?(f="before",g.bottom-=i/2):g.top+=i/2:b.left<=(d[0]+d[1])/2?(f="before",g.right-=h/2):g.left+=h/2;this.hasChildGroup(a)&&(g=w);this.rootGroup.movePlaceholder(this,c,f,g)},getItemDimensions:function(){this.itemDimensions||(this.items=this.$getChildren(this.el,"item").filter(":not(.placeholder, .dragged)").get(), +s(this.items,this.itemDimensions=[],this.options.tolerance));return this.itemDimensions},getItemOffsetParent:function(){var a=this.el;return"relative"===a.css("position")||"absolute"===a.css("position")||"fixed"===a.css("position")?a:a.offsetParent()},hasChildGroup:function(a){return this.options.nested&&this.getContainerGroup(a)},getContainerGroup:function(a){var b=e.data(this.items[a],"subContainer");if(b===h){var c=this.$getChildren(this.items[a],"container"),b=!1;c[0]&&(b=e.extend({},this.options, +{parentContainer:this,group:u++}),b=c.sortable(b).data("sortable").group);e.data(this.items[a],"subContainer",b)}return b},enabled:function(){return!this.disabled&&(!this.parentContainer||this.parentContainer.enabled())},$getChildren:function(a,b){var c=this.rootGroup.options,d=c[b+"Path"],c=c[b+"Selector"],a=e(a);d&&(a=a.find(d));return a.children(c)},_serialize:function(a,b){var c=this,d=this.$getChildren(a,b?"item":"container").not(this.options.exclude).map(function(){return c._serialize(e(this), +!b)}).get();return this.rootGroup.options.serialize(a,d,b)},clearDimensions:function(){this.itemDimensions=h;if(this.items&&this.items[0])for(var a=this.items.length;a--;){var b=e.data(this.items[a],"subContainer");b&&b.clearDimensions()}}};var q={enable:function(){this.disabled=!1},disable:function(){this.disabled=!0},serialize:function(){return this._serialize(this.el,!0)}};e.extend(n.prototype,q);e.fn.sortable=function(a){var b=Array.prototype.slice.call(arguments,1);return this.map(function(){var c= e(this),d=c.data("sortable");if(d&&q[a])return q[a].apply(d,b)||this;!d&&(a===h||"object"===typeof a)&&c.data("sortable",new n(c,a));return this})}}(jQuery,window); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/markdown/markdown.converter.js b/src/Umbraco.Web.UI.Client/lib/markdown/markdown.converter.js index 8e1af69a68..5b940dd8ee 100644 --- a/src/Umbraco.Web.UI.Client/lib/markdown/markdown.converter.js +++ b/src/Umbraco.Web.UI.Client/lib/markdown/markdown.converter.js @@ -1,1344 +1,1344 @@ -var Markdown; - -if (typeof exports === "object" && typeof require === "function") // we're in a CommonJS (e.g. Node.js) module - Markdown = exports; -else - Markdown = {}; - -// The following text is included for historical reasons, but should -// be taken with a pinch of salt; it's not all true anymore. - -// -// Wherever possible, Showdown is a straight, line-by-line port -// of the Perl version of Markdown. -// -// This is not a normal parser design; it's basically just a -// series of string substitutions. It's hard to read and -// maintain this way, but keeping Showdown close to the original -// design makes it easier to port new features. -// -// More importantly, Showdown behaves like markdown.pl in most -// edge cases. So web applications can do client-side preview -// in Javascript, and then build identical HTML on the server. -// -// This port needs the new RegExp functionality of ECMA 262, -// 3rd Edition (i.e. Javascript 1.5). Most modern web browsers -// should do fine. Even with the new regular expression features, -// We do a lot of work to emulate Perl's regex functionality. -// The tricky changes in this file mostly have the "attacklab:" -// label. Major or self-explanatory changes don't. -// -// Smart diff tools like Araxis Merge will be able to match up -// this file with markdown.pl in a useful way. A little tweaking -// helps: in a copy of markdown.pl, replace "#" with "//" and -// replace "$text" with "text". Be sure to ignore whitespace -// and line endings. -// - - -// -// Usage: -// -// var text = "Markdown *rocks*."; -// -// var converter = new Markdown.Converter(); -// var html = converter.makeHtml(text); -// -// alert(html); -// -// Note: move the sample code to the bottom of this -// file before uncommenting it. -// - -(function () { - - function identity(x) { return x; } - function returnFalse(x) { return false; } - - function HookCollection() { } - - HookCollection.prototype = { - - chain: function (hookname, func) { - var original = this[hookname]; - if (!original) - throw new Error("unknown hook " + hookname); - - if (original === identity) - this[hookname] = func; - else - this[hookname] = function (x) { return func(original(x)); } - }, - set: function (hookname, func) { - if (!this[hookname]) - throw new Error("unknown hook " + hookname); - this[hookname] = func; - }, - addNoop: function (hookname) { - this[hookname] = identity; - }, - addFalse: function (hookname) { - this[hookname] = returnFalse; - } - }; - - Markdown.HookCollection = HookCollection; - - // g_urls and g_titles allow arbitrary user-entered strings as keys. This - // caused an exception (and hence stopped the rendering) when the user entered - // e.g. [push] or [__proto__]. Adding a prefix to the actual key prevents this - // (since no builtin property starts with "s_"). See - // http://meta.stackoverflow.com/questions/64655/strange-wmd-bug - // (granted, switching from Array() to Object() alone would have left only __proto__ - // to be a problem) - function SaveHash() { } - SaveHash.prototype = { - set: function (key, value) { - this["s_" + key] = value; - }, - get: function (key) { - return this["s_" + key]; - } - }; - - Markdown.Converter = function () { - var pluginHooks = this.hooks = new HookCollection(); - pluginHooks.addNoop("plainLinkText"); // given a URL that was encountered by itself (without markup), should return the link text that's to be given to this link - pluginHooks.addNoop("preConversion"); // called with the orignal text as given to makeHtml. The result of this plugin hook is the actual markdown source that will be cooked - pluginHooks.addNoop("postConversion"); // called with the final cooked HTML code. The result of this plugin hook is the actual output of makeHtml - - // - // Private state of the converter instance: - // - - // Global hashes, used by various utility routines - var g_urls; - var g_titles; - var g_html_blocks; - - // Used to track when we're inside an ordered or unordered list - // (see _ProcessListItems() for details): - var g_list_level; - - this.makeHtml = function (text) { - - // - // Main function. The order in which other subs are called here is - // essential. Link and image substitutions need to happen before - // _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the - // and tags get encoded. - // - - // This will only happen if makeHtml on the same converter instance is called from a plugin hook. - // Don't do that. - if (g_urls) - throw new Error("Recursive call to converter.makeHtml"); - - // Create the private state objects. - g_urls = new SaveHash(); - g_titles = new SaveHash(); - g_html_blocks = []; - g_list_level = 0; - - text = pluginHooks.preConversion(text); - - // attacklab: Replace ~ with ~T - // This lets us use tilde as an escape char to avoid md5 hashes - // The choice of character is arbitray; anything that isn't - // magic in Markdown will work. - text = text.replace(/~/g, "~T"); - - // attacklab: Replace $ with ~D - // RegExp interprets $ as a special character - // when it's in a replacement string - text = text.replace(/\$/g, "~D"); - - // Standardize line endings - text = text.replace(/\r\n/g, "\n"); // DOS to Unix - text = text.replace(/\r/g, "\n"); // Mac to Unix - - // Make sure text begins and ends with a couple of newlines: - text = "\n\n" + text + "\n\n"; - - // Convert all tabs to spaces. - text = _Detab(text); - - // Strip any lines consisting only of spaces and tabs. - // This makes subsequent regexen easier to write, because we can - // match consecutive blank lines with /\n+/ instead of something - // contorted like /[ \t]*\n+/ . - text = text.replace(/^[ \t]+$/mg, ""); - - // Turn block-level HTML blocks into hash entries - text = _HashHTMLBlocks(text); - - // Strip link definitions, store in hashes. - text = _StripLinkDefinitions(text); - - text = _RunBlockGamut(text); - - text = _UnescapeSpecialChars(text); - - // attacklab: Restore dollar signs - text = text.replace(/~D/g, "$$"); - - // attacklab: Restore tildes - text = text.replace(/~T/g, "~"); - - text = pluginHooks.postConversion(text); - - g_html_blocks = g_titles = g_urls = null; - - return text; - }; - - function _StripLinkDefinitions(text) { - // - // Strips link definitions from text, stores the URLs and titles in - // hash references. - // - - // Link defs are in the form: ^[id]: url "optional title" - - /* - text = text.replace(/ - ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1 - [ \t]* - \n? // maybe *one* newline - [ \t]* - ? // url = $2 - (?=\s|$) // lookahead for whitespace instead of the lookbehind removed below - [ \t]* - \n? // maybe one newline - [ \t]* - ( // (potential) title = $3 - (\n*) // any lines skipped = $4 attacklab: lookbehind removed - [ \t]+ - ["(] - (.+?) // title = $5 - [")] - [ \t]* - )? // title is optional - (?:\n+|$) - /gm, function(){...}); - */ - - text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*?(?=\s|$)[ \t]*\n?[ \t]*((\n*)["(](.+?)[")][ \t]*)?(?:\n+)/gm, - function (wholeMatch, m1, m2, m3, m4, m5) { - m1 = m1.toLowerCase(); - g_urls.set(m1, _EncodeAmpsAndAngles(m2)); // Link IDs are case-insensitive - if (m4) { - // Oops, found blank lines, so it's not a title. - // Put back the parenthetical statement we stole. - return m3; - } else if (m5) { - g_titles.set(m1, m5.replace(/"/g, """)); - } - - // Completely remove the definition from the text - return ""; - } - ); - - return text; - } - - function _HashHTMLBlocks(text) { - - // Hashify HTML blocks: - // We only want to do this for block-level HTML tags, such as headers, - // lists, and tables. That's because we still want to wrap

    s around - // "paragraphs" that are wrapped in non-block-level tags, such as anchors, - // phrase emphasis, and spans. The list of tags we're looking for is - // hard-coded: - var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del" - var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math" - - // First, look for nested blocks, e.g.: - //

    - //
    - // tags for inner block must be indented. - //
    - //
    - // - // The outermost tags must start at the left margin for this to match, and - // the inner nested divs must be indented. - // We need to do this before the next, more liberal match, because the next - // match will start at the first `
    ` and stop at the first `
    `. - - // attacklab: This regex can be expensive when it fails. - - /* - text = text.replace(/ - ( // save in $1 - ^ // start of line (with /m) - <($block_tags_a) // start tag = $2 - \b // word break - // attacklab: hack around khtml/pcre bug... - [^\r]*?\n // any number of lines, minimally matching - // the matching end tag - [ \t]* // trailing spaces/tabs - (?=\n+) // followed by a newline - ) // attacklab: there are sentinel newlines at end of document - /gm,function(){...}}; - */ - text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm, hashElement); - - // - // Now match more liberally, simply from `\n` to `\n` - // - - /* - text = text.replace(/ - ( // save in $1 - ^ // start of line (with /m) - <($block_tags_b) // start tag = $2 - \b // word break - // attacklab: hack around khtml/pcre bug... - [^\r]*? // any number of lines, minimally matching - .* // the matching end tag - [ \t]* // trailing spaces/tabs - (?=\n+) // followed by a newline - ) // attacklab: there are sentinel newlines at end of document - /gm,function(){...}}; - */ - text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm, hashElement); - - // Special case just for
    . It was easier to make a special case than - // to make the other regex more complicated. - - /* - text = text.replace(/ - \n // Starting after a blank line - [ ]{0,3} - ( // save in $1 - (<(hr) // start tag = $2 - \b // word break - ([^<>])*? - \/?>) // the matching end tag - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,hashElement); - */ - text = text.replace(/\n[ ]{0,3}((<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g, hashElement); - - // Special case for standalone HTML comments: - - /* - text = text.replace(/ - \n\n // Starting after a blank line - [ ]{0,3} // attacklab: g_tab_width - 1 - ( // save in $1 - -]|-[^>])(?:[^-]|-[^-])*)--) // see http://www.w3.org/TR/html-markup/syntax.html#comments and http://meta.stackoverflow.com/q/95256 - > - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,hashElement); - */ - text = text.replace(/\n\n[ ]{0,3}(-]|-[^>])(?:[^-]|-[^-])*)--)>[ \t]*(?=\n{2,}))/g, hashElement); - - // PHP and ASP-style processor instructions ( and <%...%>) - - /* - text = text.replace(/ - (?: - \n\n // Starting after a blank line - ) - ( // save in $1 - [ ]{0,3} // attacklab: g_tab_width - 1 - (?: - <([?%]) // $2 - [^\r]*? - \2> - ) - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,hashElement); - */ - text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g, hashElement); - - return text; - } - - function hashElement(wholeMatch, m1) { - var blockText = m1; - - // Undo double lines - blockText = blockText.replace(/^\n+/, ""); - - // strip trailing blank lines - blockText = blockText.replace(/\n+$/g, ""); - - // Replace the element text with a marker ("~KxK" where x is its key) - blockText = "\n\n~K" + (g_html_blocks.push(blockText) - 1) + "K\n\n"; - - return blockText; - } - - function _RunBlockGamut(text, doNotUnhash) { - // - // These are all the transformations that form block-level - // tags like paragraphs, headers, and list items. - // - text = _DoHeaders(text); - - // Do Horizontal Rules: - var replacement = "
    \n"; - text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, replacement); - text = text.replace(/^[ ]{0,2}([ ]?-[ ]?){3,}[ \t]*$/gm, replacement); - text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, replacement); - - text = _DoLists(text); - text = _DoCodeBlocks(text); - text = _DoBlockQuotes(text); - - // We already ran _HashHTMLBlocks() before, in Markdown(), but that - // was to escape raw HTML in the original Markdown source. This time, - // we're escaping the markup we've just created, so that we don't wrap - //

    tags around block-level tags. - text = _HashHTMLBlocks(text); - text = _FormParagraphs(text, doNotUnhash); - - return text; - } - - function _RunSpanGamut(text) { - // - // These are all the transformations that occur *within* block-level - // tags like paragraphs, headers, and list items. - // - - text = _DoCodeSpans(text); - text = _EscapeSpecialCharsWithinTagAttributes(text); - text = _EncodeBackslashEscapes(text); - - // Process anchor and image tags. Images must come first, - // because ![foo][f] looks like an anchor. - text = _DoImages(text); - text = _DoAnchors(text); - - // Make links out of things like `` - // Must come after _DoAnchors(), because you can use < and > - // delimiters in inline links like [this](). - text = _DoAutoLinks(text); - - text = text.replace(/~P/g, "://"); // put in place to prevent autolinking; reset now - - text = _EncodeAmpsAndAngles(text); - text = _DoItalicsAndBold(text); - - // Do hard breaks: - text = text.replace(/ +\n/g, "
    \n"); - - return text; - } - - function _EscapeSpecialCharsWithinTagAttributes(text) { - // - // Within tags -- meaning between < and > -- encode [\ ` * _] so they - // don't conflict with their use in Markdown for code, italics and strong. - // - - // Build a regex to find HTML tags and comments. See Friedl's - // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. - - // SE: changed the comment part of the regex - - var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|-]|-[^>])(?:[^-]|-[^-])*)--)>)/gi; - - text = text.replace(regex, function (wholeMatch) { - var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, "$1`"); - tag = escapeCharacters(tag, wholeMatch.charAt(1) == "!" ? "\\`*_/" : "\\`*_"); // also escape slashes in comments to prevent autolinking there -- http://meta.stackoverflow.com/questions/95987 - return tag; - }); - - return text; - } - - function _DoAnchors(text) { - // - // Turn Markdown link shortcuts into XHTML
    tags. - // - // - // First, handle reference-style links: [link text] [id] - // - - /* - text = text.replace(/ - ( // wrap whole match in $1 - \[ - ( - (?: - \[[^\]]*\] // allow brackets nested one level - | - [^\[] // or anything else - )* - ) - \] - - [ ]? // one optional space - (?:\n[ ]*)? // one optional newline followed by spaces - - \[ - (.*?) // id = $3 - \] - ) - ()()()() // pad remaining backreferences - /g, writeAnchorTag); - */ - text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeAnchorTag); - - // - // Next, inline-style links: [link text](url "optional title") - // - - /* - text = text.replace(/ - ( // wrap whole match in $1 - \[ - ( - (?: - \[[^\]]*\] // allow brackets nested one level - | - [^\[\]] // or anything else - )* - ) - \] - \( // literal paren - [ \t]* - () // no id, so leave $3 empty - ? - [ \t]* - ( // $5 - (['"]) // quote char = $6 - (.*?) // Title = $7 - \6 // matching quote - [ \t]* // ignore any spaces/tabs between closing quote and ) - )? // title is optional - \) - ) - /g, writeAnchorTag); - */ - - text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeAnchorTag); - - // - // Last, handle reference-style shortcuts: [link text] - // These must come last in case you've also got [link test][1] - // or [link test](/foo) - // - - /* - text = text.replace(/ - ( // wrap whole match in $1 - \[ - ([^\[\]]+) // link text = $2; can't contain '[' or ']' - \] - ) - ()()()()() // pad rest of backreferences - /g, writeAnchorTag); - */ - text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag); - - return text; - } - - function writeAnchorTag(wholeMatch, m1, m2, m3, m4, m5, m6, m7) { - if (m7 == undefined) m7 = ""; - var whole_match = m1; - var link_text = m2.replace(/:\/\//g, "~P"); // to prevent auto-linking withing the link. will be converted back after the auto-linker runs - var link_id = m3.toLowerCase(); - var url = m4; - var title = m7; - - if (url == "") { - if (link_id == "") { - // lower-case and turn embedded newlines into spaces - link_id = link_text.toLowerCase().replace(/ ?\n/g, " "); - } - url = "#" + link_id; - - if (g_urls.get(link_id) != undefined) { - url = g_urls.get(link_id); - if (g_titles.get(link_id) != undefined) { - title = g_titles.get(link_id); - } - } - else { - if (whole_match.search(/\(\s*\)$/m) > -1) { - // Special case for explicit empty url - url = ""; - } else { - return whole_match; - } - } - } - url = encodeProblemUrlChars(url); - url = escapeCharacters(url, "*_"); - var result = ""; - - return result; - } - - function _DoImages(text) { - // - // Turn Markdown image shortcuts into tags. - // - - // - // First, handle reference-style labeled images: ![alt text][id] - // - - /* - text = text.replace(/ - ( // wrap whole match in $1 - !\[ - (.*?) // alt text = $2 - \] - - [ ]? // one optional space - (?:\n[ ]*)? // one optional newline followed by spaces - - \[ - (.*?) // id = $3 - \] - ) - ()()()() // pad rest of backreferences - /g, writeImageTag); - */ - text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeImageTag); - - // - // Next, handle inline images: ![alt text](url "optional title") - // Don't forget: encode * and _ - - /* - text = text.replace(/ - ( // wrap whole match in $1 - !\[ - (.*?) // alt text = $2 - \] - \s? // One optional whitespace character - \( // literal paren - [ \t]* - () // no id, so leave $3 empty - ? // src url = $4 - [ \t]* - ( // $5 - (['"]) // quote char = $6 - (.*?) // title = $7 - \6 // matching quote - [ \t]* - )? // title is optional - \) - ) - /g, writeImageTag); - */ - text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeImageTag); - - return text; - } - - function attributeEncode(text) { - // unconditionally replace angle brackets here -- what ends up in an attribute (e.g. alt or title) - // never makes sense to have verbatim HTML in it (and the sanitizer would totally break it) - return text.replace(/>/g, ">").replace(/" + _RunSpanGamut(m1) + "\n\n"; } - ); - - text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm, - function (matchFound, m1) { return "

    " + _RunSpanGamut(m1) + "

    \n\n"; } - ); - - // atx-style headers: - // # Header 1 - // ## Header 2 - // ## Header 2 with closing hashes ## - // ... - // ###### Header 6 - // - - /* - text = text.replace(/ - ^(\#{1,6}) // $1 = string of #'s - [ \t]* - (.+?) // $2 = Header text - [ \t]* - \#* // optional closing #'s (not counted) - \n+ - /gm, function() {...}); - */ - - text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm, - function (wholeMatch, m1, m2) { - var h_level = m1.length; - return "" + _RunSpanGamut(m2) + "\n\n"; - } - ); - - return text; - } - - function _DoLists(text) { - // - // Form HTML ordered (numbered) and unordered (bulleted) lists. - // - - // attacklab: add sentinel to hack around khtml/safari bug: - // http://bugs.webkit.org/show_bug.cgi?id=11231 - text += "~0"; - - // Re-usable pattern to match any entirel ul or ol list: - - /* - var whole_list = / - ( // $1 = whole list - ( // $2 - [ ]{0,3} // attacklab: g_tab_width - 1 - ([*+-]|\d+[.]) // $3 = first list item marker - [ \t]+ - ) - [^\r]+? - ( // $4 - ~0 // sentinel for workaround; should be $ - | - \n{2,} - (?=\S) - (?! // Negative lookahead for another list item marker - [ \t]* - (?:[*+-]|\d+[.])[ \t]+ - ) - ) - ) - /g - */ - var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; - - if (g_list_level) { - text = text.replace(whole_list, function (wholeMatch, m1, m2) { - var list = m1; - var list_type = (m2.search(/[*+-]/g) > -1) ? "ul" : "ol"; - - var result = _ProcessListItems(list, list_type); - - // Trim any trailing whitespace, to put the closing `` - // up on the preceding line, to get it past the current stupid - // HTML block parser. This is a hack to work around the terrible - // hack that is the HTML block parser. - result = result.replace(/\s+$/, ""); - result = "<" + list_type + ">" + result + "\n"; - return result; - }); - } else { - whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g; - text = text.replace(whole_list, function (wholeMatch, m1, m2, m3) { - var runup = m1; - var list = m2; - - var list_type = (m3.search(/[*+-]/g) > -1) ? "ul" : "ol"; - var result = _ProcessListItems(list, list_type); - result = runup + "<" + list_type + ">\n" + result + "\n"; - return result; - }); - } - - // attacklab: strip sentinel - text = text.replace(/~0/, ""); - - return text; - } - - var _listItemMarkers = { ol: "\\d+[.]", ul: "[*+-]" }; - - function _ProcessListItems(list_str, list_type) { - // - // Process the contents of a single ordered or unordered list, splitting it - // into individual list items. - // - // list_type is either "ul" or "ol". - - // The $g_list_level global keeps track of when we're inside a list. - // Each time we enter a list, we increment it; when we leave a list, - // we decrement. If it's zero, we're not in a list anymore. - // - // We do this because when we're not inside a list, we want to treat - // something like this: - // - // I recommend upgrading to version - // 8. Oops, now this line is treated - // as a sub-list. - // - // As a single paragraph, despite the fact that the second line starts - // with a digit-period-space sequence. - // - // Whereas when we're inside a list (or sub-list), that line will be - // treated as the start of a sub-list. What a kludge, huh? This is - // an aspect of Markdown's syntax that's hard to parse perfectly - // without resorting to mind-reading. Perhaps the solution is to - // change the syntax rules such that sub-lists must start with a - // starting cardinal number; e.g. "1." or "a.". - - g_list_level++; - - // trim trailing blank lines: - list_str = list_str.replace(/\n{2,}$/, "\n"); - - // attacklab: add sentinel to emulate \z - list_str += "~0"; - - // In the original attacklab showdown, list_type was not given to this function, and anything - // that matched /[*+-]|\d+[.]/ would just create the next
  • , causing this mismatch: - // - // Markdown rendered by WMD rendered by MarkdownSharp - // ------------------------------------------------------------------ - // 1. first 1. first 1. first - // 2. second 2. second 2. second - // - third 3. third * third - // - // We changed this to behave identical to MarkdownSharp. This is the constructed RegEx, - // with {MARKER} being one of \d+[.] or [*+-], depending on list_type: - - /* - list_str = list_str.replace(/ - (^[ \t]*) // leading whitespace = $1 - ({MARKER}) [ \t]+ // list marker = $2 - ([^\r]+? // list item text = $3 - (\n+) - ) - (?= - (~0 | \2 ({MARKER}) [ \t]+) - ) - /gm, function(){...}); - */ - - var marker = _listItemMarkers[list_type]; - var re = new RegExp("(^[ \\t]*)(" + marker + ")[ \\t]+([^\\r]+?(\\n+))(?=(~0|\\1(" + marker + ")[ \\t]+))", "gm"); - var last_item_had_a_double_newline = false; - list_str = list_str.replace(re, - function (wholeMatch, m1, m2, m3) { - var item = m3; - var leading_space = m1; - var ends_with_double_newline = /\n\n$/.test(item); - var contains_double_newline = ends_with_double_newline || item.search(/\n{2,}/) > -1; - - if (contains_double_newline || last_item_had_a_double_newline) { - item = _RunBlockGamut(_Outdent(item), /* doNotUnhash = */true); - } - else { - // Recursion for sub-lists: - item = _DoLists(_Outdent(item)); - item = item.replace(/\n$/, ""); // chomp(item) - item = _RunSpanGamut(item); - } - last_item_had_a_double_newline = ends_with_double_newline; - return "
  • " + item + "
  • \n"; - } - ); - - // attacklab: strip sentinel - list_str = list_str.replace(/~0/g, ""); - - g_list_level--; - return list_str; - } - - function _DoCodeBlocks(text) { - // - // Process Markdown `
    ` blocks.
    -			//  
    -
    -			/*
    -            text = text.replace(/
    -                (?:\n\n|^)
    -                (                               // $1 = the code block -- one or more lines, starting with a space/tab
    -                    (?:
    -                        (?:[ ]{4}|\t)           // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
    -                        .*\n+
    -                    )+
    -                )
    -                (\n*[ ]{0,3}[^ \t\n]|(?=~0))    // attacklab: g_tab_width
    -            /g ,function(){...});
    -            */
    -
    -			// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
    -			text += "~0";
    -
    -			text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
    -                function (wholeMatch, m1, m2) {
    -                	var codeblock = m1;
    -                	var nextChar = m2;
    -
    -                	codeblock = _EncodeCode(_Outdent(codeblock));
    -                	codeblock = _Detab(codeblock);
    -                	codeblock = codeblock.replace(/^\n+/g, ""); // trim leading newlines
    -                	codeblock = codeblock.replace(/\n+$/g, ""); // trim trailing whitespace
    -
    -                	codeblock = '
    ' + codeblock + '\n
    '; - - return "\n\n" + codeblock + "\n\n" + nextChar; - } - ); - - // attacklab: strip sentinel - text = text.replace(/~0/, ""); - - return text; - } - - function hashBlock(text) { - text = text.replace(/(^\n+|\n+$)/g, ""); - return "\n\n~K" + (g_html_blocks.push(text) - 1) + "K\n\n"; - } - - function _DoCodeSpans(text) { - // - // * Backtick quotes are used for spans. - // - // * You can use multiple backticks as the delimiters if you want to - // include literal backticks in the code span. So, this input: - // - // Just type ``foo `bar` baz`` at the prompt. - // - // Will translate to: - // - //

    Just type foo `bar` baz at the prompt.

    - // - // There's no arbitrary limit to the number of backticks you - // can use as delimters. If you need three consecutive backticks - // in your code, use four for delimiters, etc. - // - // * You can use spaces to get literal backticks at the edges: - // - // ... type `` `bar` `` ... - // - // Turns to: - // - // ... type `bar` ... - // - - /* - text = text.replace(/ - (^|[^\\]) // Character before opening ` can't be a backslash - (`+) // $2 = Opening run of ` - ( // $3 = The code block - [^\r]*? - [^`] // attacklab: work around lack of lookbehind - ) - \2 // Matching closer - (?!`) - /gm, function(){...}); - */ - - text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, - function (wholeMatch, m1, m2, m3, m4) { - var c = m3; - c = c.replace(/^([ \t]*)/g, ""); // leading whitespace - c = c.replace(/[ \t]*$/g, ""); // trailing whitespace - c = _EncodeCode(c); - c = c.replace(/:\/\//g, "~P"); // to prevent auto-linking. Not necessary in code *blocks*, but in code spans. Will be converted back after the auto-linker runs. - return m1 + "" + c + ""; - } - ); - - return text; - } - - function _EncodeCode(text) { - // - // Encode/escape certain characters inside Markdown code runs. - // The point is that in code, these characters are literals, - // and lose their special Markdown meanings. - // - // Encode all ampersands; HTML entities are not - // entities within a Markdown code span. - text = text.replace(/&/g, "&"); - - // Do the angle bracket song and dance: - text = text.replace(//g, ">"); - - // Now, escape characters that are magic in Markdown: - text = escapeCharacters(text, "\*_{}[]\\", false); - - // jj the line above breaks this: - //--- - - //* Item - - // 1. Subitem - - // special char: * - //--- - - return text; - } - - function _DoItalicsAndBold(text) { - - // must go first: - text = text.replace(/([\W_]|^)(\*\*|__)(?=\S)([^\r]*?\S[\*_]*)\2([\W_]|$)/g, - "$1$3$4"); - - text = text.replace(/([\W_]|^)(\*|_)(?=\S)([^\r\*_]*?\S)\2([\W_]|$)/g, - "$1$3$4"); - - return text; - } - - function _DoBlockQuotes(text) { - - /* - text = text.replace(/ - ( // Wrap whole match in $1 - ( - ^[ \t]*>[ \t]? // '>' at the start of a line - .+\n // rest of the first line - (.+\n)* // subsequent consecutive lines - \n* // blanks - )+ - ) - /gm, function(){...}); - */ - - text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm, - function (wholeMatch, m1) { - var bq = m1; - - // attacklab: hack around Konqueror 3.5.4 bug: - // "----------bug".replace(/^-/g,"") == "bug" - - bq = bq.replace(/^[ \t]*>[ \t]?/gm, "~0"); // trim one level of quoting - - // attacklab: clean up hack - bq = bq.replace(/~0/g, ""); - - bq = bq.replace(/^[ \t]+$/gm, ""); // trim whitespace-only lines - bq = _RunBlockGamut(bq); // recurse - - bq = bq.replace(/(^|\n)/g, "$1 "); - // These leading spaces screw with
     content, so we need to fix that:
    -                	bq = bq.replace(
    -                            /(\s*
    [^\r]+?<\/pre>)/gm,
    -                        function (wholeMatch, m1) {
    -                        	var pre = m1;
    -                        	// attacklab: hack around Konqueror 3.5.4 bug:
    -                        	pre = pre.replace(/^  /mg, "~0");
    -                        	pre = pre.replace(/~0/g, "");
    -                        	return pre;
    -                        });
    -
    -                	return hashBlock("
    \n" + bq + "\n
    "); - } - ); - return text; - } - - function _FormParagraphs(text, doNotUnhash) { - // - // Params: - // $text - string to process with html

    tags - // - - // Strip leading and trailing lines: - text = text.replace(/^\n+/g, ""); - text = text.replace(/\n+$/g, ""); - - var grafs = text.split(/\n{2,}/g); - var grafsOut = []; - - var markerRe = /~K(\d+)K/; - - // - // Wrap

    tags. - // - var end = grafs.length; - for (var i = 0; i < end; i++) { - var str = grafs[i]; - - // if this is an HTML marker, copy it - if (markerRe.test(str)) { - grafsOut.push(str); - } - else if (/\S/.test(str)) { - str = _RunSpanGamut(str); - str = str.replace(/^([ \t]*)/g, "

    "); - str += "

    " - grafsOut.push(str); - } - - } - // - // Unhashify HTML blocks - // - if (!doNotUnhash) { - end = grafsOut.length; - for (var i = 0; i < end; i++) { - var foundAny = true; - while (foundAny) { // we may need several runs, since the data may be nested - foundAny = false; - grafsOut[i] = grafsOut[i].replace(/~K(\d+)K/g, function (wholeMatch, id) { - foundAny = true; - return g_html_blocks[id]; - }); - } - } - } - return grafsOut.join("\n\n"); - } - - function _EncodeAmpsAndAngles(text) { - // Smart processing for ampersands and angle brackets that need to be encoded. - - // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: - // http://bumppo.net/projects/amputator/ - text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, "&"); - - // Encode naked <'s - text = text.replace(/<(?![a-z\/?\$!])/gi, "<"); - - return text; - } - - function _EncodeBackslashEscapes(text) { - // - // Parameter: String. - // Returns: The string, with after processing the following backslash - // escape sequences. - // - - // attacklab: The polite way to do this is with the new - // escapeCharacters() function: - // - // text = escapeCharacters(text,"\\",true); - // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true); - // - // ...but we're sidestepping its use of the (slow) RegExp constructor - // as an optimization for Firefox. This function gets called a LOT. - - text = text.replace(/\\(\\)/g, escapeCharacters_callback); - text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, escapeCharacters_callback); - return text; - } - - function _DoAutoLinks(text) { - - // note that at this point, all other URL in the text are already hyperlinked as
    - // *except* for the case - - // automatically add < and > around unadorned raw hyperlinks - // must be preceded by space/BOF and followed by non-word/EOF character - text = text.replace(/(^|\s)(https?|ftp)(:\/\/[-A-Z0-9+&@#\/%?=~_|\[\]\(\)!:,\.;]*[-A-Z0-9+&@#\/%=~_|\[\]])($|\W)/gi, "$1<$2$3>$4"); - - // autolink anything like - - var replacer = function (wholematch, m1) { return "" + pluginHooks.plainLinkText(m1) + ""; } - text = text.replace(/<((https?|ftp):[^'">\s]+)>/gi, replacer); - - // Email addresses: - /* - text = text.replace(/ - < - (?:mailto:)? - ( - [-.\w]+ - \@ - [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ - ) - > - /gi, _DoAutoLinks_callback()); - */ - - var email_replacer = function (wholematch, m1) { - var mailto = 'mailto:' - var link - var email - if (m1.substring(0, mailto.length) != mailto) { - link = mailto + m1; - email = m1; - } else { - link = m1; - email = m1.substring(mailto.length, m1.length); - } - return "" + pluginHooks.plainLinkText(email) + ""; - } - text = text.replace(/<((?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+))>/gi, email_replacer); - - return text; - } - - function _UnescapeSpecialChars(text) { - // - // Swap back in all the special characters we've hidden. - // - text = text.replace(/~E(\d+)E/g, - function (wholeMatch, m1) { - var charCodeToReplace = parseInt(m1); - return String.fromCharCode(charCodeToReplace); - } - ); - return text; - } - - function _Outdent(text) { - // - // Remove one level of line-leading tabs or spaces - // - - // attacklab: hack around Konqueror 3.5.4 bug: - // "----------bug".replace(/^-/g,"") == "bug" - - text = text.replace(/^(\t|[ ]{1,4})/gm, "~0"); // attacklab: g_tab_width - - // attacklab: clean up hack - text = text.replace(/~0/g, "") - - return text; - } - - function _Detab(text) { - if (!/\t/.test(text)) - return text; - - var spaces = [" ", " ", " ", " "], - skew = 0, - v; - - return text.replace(/[\n\t]/g, function (match, offset) { - if (match === "\n") { - skew = offset + 1; - return match; - } - v = (offset - skew) % 4; - skew = offset + 1; - return spaces[v]; - }); - } - - // - // attacklab: Utility functions - // - - var _problemUrlChars = /(?:["'*()[\]:]|~D)/g; - - // hex-encodes some unusual "problem" chars in URLs to avoid URL detection problems - function encodeProblemUrlChars(url) { - if (!url) - return ""; - - var len = url.length; - - return url.replace(_problemUrlChars, function (match, offset) { - if (match == "~D") // escape for dollar - return "%24"; - if (match == ":") { - if (offset == len - 1 || /[0-9\/]/.test(url.charAt(offset + 1))) - return ":"; - if (url.substring(0, 'mailto:'.length) === 'mailto:') - return ":"; - if (url.substring(0, 'magnet:'.length) === 'magnet:') - return ":"; - } - return "%" + match.charCodeAt(0).toString(16); - }); - } - - - function escapeCharacters(text, charsToEscape, afterBackslash) { - // First we have to escape the escape characters so that - // we can build a character class out of them - var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g, "\\$1") + "])"; - - if (afterBackslash) { - regexString = "\\\\" + regexString; - } - - var regex = new RegExp(regexString, "g"); - text = text.replace(regex, escapeCharacters_callback); - - return text; - } - - - function escapeCharacters_callback(wholeMatch, m1) { - var charCodeToEscape = m1.charCodeAt(0); - return "~E" + charCodeToEscape + "E"; - } - - }; // end of the Markdown.Converter constructor - +var Markdown; + +if (typeof exports === "object" && typeof require === "function") // we're in a CommonJS (e.g. Node.js) module + Markdown = exports; +else + Markdown = {}; + +// The following text is included for historical reasons, but should +// be taken with a pinch of salt; it's not all true anymore. + +// +// Wherever possible, Showdown is a straight, line-by-line port +// of the Perl version of Markdown. +// +// This is not a normal parser design; it's basically just a +// series of string substitutions. It's hard to read and +// maintain this way, but keeping Showdown close to the original +// design makes it easier to port new features. +// +// More importantly, Showdown behaves like markdown.pl in most +// edge cases. So web applications can do client-side preview +// in Javascript, and then build identical HTML on the server. +// +// This port needs the new RegExp functionality of ECMA 262, +// 3rd Edition (i.e. Javascript 1.5). Most modern web browsers +// should do fine. Even with the new regular expression features, +// We do a lot of work to emulate Perl's regex functionality. +// The tricky changes in this file mostly have the "attacklab:" +// label. Major or self-explanatory changes don't. +// +// Smart diff tools like Araxis Merge will be able to match up +// this file with markdown.pl in a useful way. A little tweaking +// helps: in a copy of markdown.pl, replace "#" with "//" and +// replace "$text" with "text". Be sure to ignore whitespace +// and line endings. +// + + +// +// Usage: +// +// var text = "Markdown *rocks*."; +// +// var converter = new Markdown.Converter(); +// var html = converter.makeHtml(text); +// +// alert(html); +// +// Note: move the sample code to the bottom of this +// file before uncommenting it. +// + +(function () { + + function identity(x) { return x; } + function returnFalse(x) { return false; } + + function HookCollection() { } + + HookCollection.prototype = { + + chain: function (hookname, func) { + var original = this[hookname]; + if (!original) + throw new Error("unknown hook " + hookname); + + if (original === identity) + this[hookname] = func; + else + this[hookname] = function (x) { return func(original(x)); } + }, + set: function (hookname, func) { + if (!this[hookname]) + throw new Error("unknown hook " + hookname); + this[hookname] = func; + }, + addNoop: function (hookname) { + this[hookname] = identity; + }, + addFalse: function (hookname) { + this[hookname] = returnFalse; + } + }; + + Markdown.HookCollection = HookCollection; + + // g_urls and g_titles allow arbitrary user-entered strings as keys. This + // caused an exception (and hence stopped the rendering) when the user entered + // e.g. [push] or [__proto__]. Adding a prefix to the actual key prevents this + // (since no builtin property starts with "s_"). See + // http://meta.stackoverflow.com/questions/64655/strange-wmd-bug + // (granted, switching from Array() to Object() alone would have left only __proto__ + // to be a problem) + function SaveHash() { } + SaveHash.prototype = { + set: function (key, value) { + this["s_" + key] = value; + }, + get: function (key) { + return this["s_" + key]; + } + }; + + Markdown.Converter = function () { + var pluginHooks = this.hooks = new HookCollection(); + pluginHooks.addNoop("plainLinkText"); // given a URL that was encountered by itself (without markup), should return the link text that's to be given to this link + pluginHooks.addNoop("preConversion"); // called with the orignal text as given to makeHtml. The result of this plugin hook is the actual markdown source that will be cooked + pluginHooks.addNoop("postConversion"); // called with the final cooked HTML code. The result of this plugin hook is the actual output of makeHtml + + // + // Private state of the converter instance: + // + + // Global hashes, used by various utility routines + var g_urls; + var g_titles; + var g_html_blocks; + + // Used to track when we're inside an ordered or unordered list + // (see _ProcessListItems() for details): + var g_list_level; + + this.makeHtml = function (text) { + + // + // Main function. The order in which other subs are called here is + // essential. Link and image substitutions need to happen before + // _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the + // and tags get encoded. + // + + // This will only happen if makeHtml on the same converter instance is called from a plugin hook. + // Don't do that. + if (g_urls) + throw new Error("Recursive call to converter.makeHtml"); + + // Create the private state objects. + g_urls = new SaveHash(); + g_titles = new SaveHash(); + g_html_blocks = []; + g_list_level = 0; + + text = pluginHooks.preConversion(text); + + // attacklab: Replace ~ with ~T + // This lets us use tilde as an escape char to avoid md5 hashes + // The choice of character is arbitray; anything that isn't + // magic in Markdown will work. + text = text.replace(/~/g, "~T"); + + // attacklab: Replace $ with ~D + // RegExp interprets $ as a special character + // when it's in a replacement string + text = text.replace(/\$/g, "~D"); + + // Standardize line endings + text = text.replace(/\r\n/g, "\n"); // DOS to Unix + text = text.replace(/\r/g, "\n"); // Mac to Unix + + // Make sure text begins and ends with a couple of newlines: + text = "\n\n" + text + "\n\n"; + + // Convert all tabs to spaces. + text = _Detab(text); + + // Strip any lines consisting only of spaces and tabs. + // This makes subsequent regexen easier to write, because we can + // match consecutive blank lines with /\n+/ instead of something + // contorted like /[ \t]*\n+/ . + text = text.replace(/^[ \t]+$/mg, ""); + + // Turn block-level HTML blocks into hash entries + text = _HashHTMLBlocks(text); + + // Strip link definitions, store in hashes. + text = _StripLinkDefinitions(text); + + text = _RunBlockGamut(text); + + text = _UnescapeSpecialChars(text); + + // attacklab: Restore dollar signs + text = text.replace(/~D/g, "$$"); + + // attacklab: Restore tildes + text = text.replace(/~T/g, "~"); + + text = pluginHooks.postConversion(text); + + g_html_blocks = g_titles = g_urls = null; + + return text; + }; + + function _StripLinkDefinitions(text) { + // + // Strips link definitions from text, stores the URLs and titles in + // hash references. + // + + // Link defs are in the form: ^[id]: url "optional title" + + /* + text = text.replace(/ + ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1 + [ \t]* + \n? // maybe *one* newline + [ \t]* + ? // url = $2 + (?=\s|$) // lookahead for whitespace instead of the lookbehind removed below + [ \t]* + \n? // maybe one newline + [ \t]* + ( // (potential) title = $3 + (\n*) // any lines skipped = $4 attacklab: lookbehind removed + [ \t]+ + ["(] + (.+?) // title = $5 + [")] + [ \t]* + )? // title is optional + (?:\n+|$) + /gm, function(){...}); + */ + + text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*?(?=\s|$)[ \t]*\n?[ \t]*((\n*)["(](.+?)[")][ \t]*)?(?:\n+)/gm, + function (wholeMatch, m1, m2, m3, m4, m5) { + m1 = m1.toLowerCase(); + g_urls.set(m1, _EncodeAmpsAndAngles(m2)); // Link IDs are case-insensitive + if (m4) { + // Oops, found blank lines, so it's not a title. + // Put back the parenthetical statement we stole. + return m3; + } else if (m5) { + g_titles.set(m1, m5.replace(/"/g, """)); + } + + // Completely remove the definition from the text + return ""; + } + ); + + return text; + } + + function _HashHTMLBlocks(text) { + + // Hashify HTML blocks: + // We only want to do this for block-level HTML tags, such as headers, + // lists, and tables. That's because we still want to wrap

    s around + // "paragraphs" that are wrapped in non-block-level tags, such as anchors, + // phrase emphasis, and spans. The list of tags we're looking for is + // hard-coded: + var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del" + var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math" + + // First, look for nested blocks, e.g.: + //

    + //
    + // tags for inner block must be indented. + //
    + //
    + // + // The outermost tags must start at the left margin for this to match, and + // the inner nested divs must be indented. + // We need to do this before the next, more liberal match, because the next + // match will start at the first `
    ` and stop at the first `
    `. + + // attacklab: This regex can be expensive when it fails. + + /* + text = text.replace(/ + ( // save in $1 + ^ // start of line (with /m) + <($block_tags_a) // start tag = $2 + \b // word break + // attacklab: hack around khtml/pcre bug... + [^\r]*?\n // any number of lines, minimally matching + // the matching end tag + [ \t]* // trailing spaces/tabs + (?=\n+) // followed by a newline + ) // attacklab: there are sentinel newlines at end of document + /gm,function(){...}}; + */ + text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm, hashElement); + + // + // Now match more liberally, simply from `\n` to `\n` + // + + /* + text = text.replace(/ + ( // save in $1 + ^ // start of line (with /m) + <($block_tags_b) // start tag = $2 + \b // word break + // attacklab: hack around khtml/pcre bug... + [^\r]*? // any number of lines, minimally matching + .* // the matching end tag + [ \t]* // trailing spaces/tabs + (?=\n+) // followed by a newline + ) // attacklab: there are sentinel newlines at end of document + /gm,function(){...}}; + */ + text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm, hashElement); + + // Special case just for
    . It was easier to make a special case than + // to make the other regex more complicated. + + /* + text = text.replace(/ + \n // Starting after a blank line + [ ]{0,3} + ( // save in $1 + (<(hr) // start tag = $2 + \b // word break + ([^<>])*? + \/?>) // the matching end tag + [ \t]* + (?=\n{2,}) // followed by a blank line + ) + /g,hashElement); + */ + text = text.replace(/\n[ ]{0,3}((<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g, hashElement); + + // Special case for standalone HTML comments: + + /* + text = text.replace(/ + \n\n // Starting after a blank line + [ ]{0,3} // attacklab: g_tab_width - 1 + ( // save in $1 + -]|-[^>])(?:[^-]|-[^-])*)--) // see http://www.w3.org/TR/html-markup/syntax.html#comments and http://meta.stackoverflow.com/q/95256 + > + [ \t]* + (?=\n{2,}) // followed by a blank line + ) + /g,hashElement); + */ + text = text.replace(/\n\n[ ]{0,3}(-]|-[^>])(?:[^-]|-[^-])*)--)>[ \t]*(?=\n{2,}))/g, hashElement); + + // PHP and ASP-style processor instructions ( and <%...%>) + + /* + text = text.replace(/ + (?: + \n\n // Starting after a blank line + ) + ( // save in $1 + [ ]{0,3} // attacklab: g_tab_width - 1 + (?: + <([?%]) // $2 + [^\r]*? + \2> + ) + [ \t]* + (?=\n{2,}) // followed by a blank line + ) + /g,hashElement); + */ + text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g, hashElement); + + return text; + } + + function hashElement(wholeMatch, m1) { + var blockText = m1; + + // Undo double lines + blockText = blockText.replace(/^\n+/, ""); + + // strip trailing blank lines + blockText = blockText.replace(/\n+$/g, ""); + + // Replace the element text with a marker ("~KxK" where x is its key) + blockText = "\n\n~K" + (g_html_blocks.push(blockText) - 1) + "K\n\n"; + + return blockText; + } + + function _RunBlockGamut(text, doNotUnhash) { + // + // These are all the transformations that form block-level + // tags like paragraphs, headers, and list items. + // + text = _DoHeaders(text); + + // Do Horizontal Rules: + var replacement = "
    \n"; + text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, replacement); + text = text.replace(/^[ ]{0,2}([ ]?-[ ]?){3,}[ \t]*$/gm, replacement); + text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, replacement); + + text = _DoLists(text); + text = _DoCodeBlocks(text); + text = _DoBlockQuotes(text); + + // We already ran _HashHTMLBlocks() before, in Markdown(), but that + // was to escape raw HTML in the original Markdown source. This time, + // we're escaping the markup we've just created, so that we don't wrap + //

    tags around block-level tags. + text = _HashHTMLBlocks(text); + text = _FormParagraphs(text, doNotUnhash); + + return text; + } + + function _RunSpanGamut(text) { + // + // These are all the transformations that occur *within* block-level + // tags like paragraphs, headers, and list items. + // + + text = _DoCodeSpans(text); + text = _EscapeSpecialCharsWithinTagAttributes(text); + text = _EncodeBackslashEscapes(text); + + // Process anchor and image tags. Images must come first, + // because ![foo][f] looks like an anchor. + text = _DoImages(text); + text = _DoAnchors(text); + + // Make links out of things like `` + // Must come after _DoAnchors(), because you can use < and > + // delimiters in inline links like [this](). + text = _DoAutoLinks(text); + + text = text.replace(/~P/g, "://"); // put in place to prevent autolinking; reset now + + text = _EncodeAmpsAndAngles(text); + text = _DoItalicsAndBold(text); + + // Do hard breaks: + text = text.replace(/ +\n/g, "
    \n"); + + return text; + } + + function _EscapeSpecialCharsWithinTagAttributes(text) { + // + // Within tags -- meaning between < and > -- encode [\ ` * _] so they + // don't conflict with their use in Markdown for code, italics and strong. + // + + // Build a regex to find HTML tags and comments. See Friedl's + // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. + + // SE: changed the comment part of the regex + + var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|-]|-[^>])(?:[^-]|-[^-])*)--)>)/gi; + + text = text.replace(regex, function (wholeMatch) { + var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, "$1`"); + tag = escapeCharacters(tag, wholeMatch.charAt(1) == "!" ? "\\`*_/" : "\\`*_"); // also escape slashes in comments to prevent autolinking there -- http://meta.stackoverflow.com/questions/95987 + return tag; + }); + + return text; + } + + function _DoAnchors(text) { + // + // Turn Markdown link shortcuts into XHTML
    tags. + // + // + // First, handle reference-style links: [link text] [id] + // + + /* + text = text.replace(/ + ( // wrap whole match in $1 + \[ + ( + (?: + \[[^\]]*\] // allow brackets nested one level + | + [^\[] // or anything else + )* + ) + \] + + [ ]? // one optional space + (?:\n[ ]*)? // one optional newline followed by spaces + + \[ + (.*?) // id = $3 + \] + ) + ()()()() // pad remaining backreferences + /g, writeAnchorTag); + */ + text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeAnchorTag); + + // + // Next, inline-style links: [link text](url "optional title") + // + + /* + text = text.replace(/ + ( // wrap whole match in $1 + \[ + ( + (?: + \[[^\]]*\] // allow brackets nested one level + | + [^\[\]] // or anything else + )* + ) + \] + \( // literal paren + [ \t]* + () // no id, so leave $3 empty + ? + [ \t]* + ( // $5 + (['"]) // quote char = $6 + (.*?) // Title = $7 + \6 // matching quote + [ \t]* // ignore any spaces/tabs between closing quote and ) + )? // title is optional + \) + ) + /g, writeAnchorTag); + */ + + text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeAnchorTag); + + // + // Last, handle reference-style shortcuts: [link text] + // These must come last in case you've also got [link test][1] + // or [link test](/foo) + // + + /* + text = text.replace(/ + ( // wrap whole match in $1 + \[ + ([^\[\]]+) // link text = $2; can't contain '[' or ']' + \] + ) + ()()()()() // pad rest of backreferences + /g, writeAnchorTag); + */ + text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag); + + return text; + } + + function writeAnchorTag(wholeMatch, m1, m2, m3, m4, m5, m6, m7) { + if (m7 == undefined) m7 = ""; + var whole_match = m1; + var link_text = m2.replace(/:\/\//g, "~P"); // to prevent auto-linking withing the link. will be converted back after the auto-linker runs + var link_id = m3.toLowerCase(); + var url = m4; + var title = m7; + + if (url == "") { + if (link_id == "") { + // lower-case and turn embedded newlines into spaces + link_id = link_text.toLowerCase().replace(/ ?\n/g, " "); + } + url = "#" + link_id; + + if (g_urls.get(link_id) != undefined) { + url = g_urls.get(link_id); + if (g_titles.get(link_id) != undefined) { + title = g_titles.get(link_id); + } + } + else { + if (whole_match.search(/\(\s*\)$/m) > -1) { + // Special case for explicit empty url + url = ""; + } else { + return whole_match; + } + } + } + url = encodeProblemUrlChars(url); + url = escapeCharacters(url, "*_"); + var result = ""; + + return result; + } + + function _DoImages(text) { + // + // Turn Markdown image shortcuts into tags. + // + + // + // First, handle reference-style labeled images: ![alt text][id] + // + + /* + text = text.replace(/ + ( // wrap whole match in $1 + !\[ + (.*?) // alt text = $2 + \] + + [ ]? // one optional space + (?:\n[ ]*)? // one optional newline followed by spaces + + \[ + (.*?) // id = $3 + \] + ) + ()()()() // pad rest of backreferences + /g, writeImageTag); + */ + text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeImageTag); + + // + // Next, handle inline images: ![alt text](url "optional title") + // Don't forget: encode * and _ + + /* + text = text.replace(/ + ( // wrap whole match in $1 + !\[ + (.*?) // alt text = $2 + \] + \s? // One optional whitespace character + \( // literal paren + [ \t]* + () // no id, so leave $3 empty + ? // src url = $4 + [ \t]* + ( // $5 + (['"]) // quote char = $6 + (.*?) // title = $7 + \6 // matching quote + [ \t]* + )? // title is optional + \) + ) + /g, writeImageTag); + */ + text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeImageTag); + + return text; + } + + function attributeEncode(text) { + // unconditionally replace angle brackets here -- what ends up in an attribute (e.g. alt or title) + // never makes sense to have verbatim HTML in it (and the sanitizer would totally break it) + return text.replace(/>/g, ">").replace(/" + _RunSpanGamut(m1) + "\n\n"; } + ); + + text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm, + function (matchFound, m1) { return "

    " + _RunSpanGamut(m1) + "

    \n\n"; } + ); + + // atx-style headers: + // # Header 1 + // ## Header 2 + // ## Header 2 with closing hashes ## + // ... + // ###### Header 6 + // + + /* + text = text.replace(/ + ^(\#{1,6}) // $1 = string of #'s + [ \t]* + (.+?) // $2 = Header text + [ \t]* + \#* // optional closing #'s (not counted) + \n+ + /gm, function() {...}); + */ + + text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm, + function (wholeMatch, m1, m2) { + var h_level = m1.length; + return "" + _RunSpanGamut(m2) + "\n\n"; + } + ); + + return text; + } + + function _DoLists(text) { + // + // Form HTML ordered (numbered) and unordered (bulleted) lists. + // + + // attacklab: add sentinel to hack around khtml/safari bug: + // http://bugs.webkit.org/show_bug.cgi?id=11231 + text += "~0"; + + // Re-usable pattern to match any entirel ul or ol list: + + /* + var whole_list = / + ( // $1 = whole list + ( // $2 + [ ]{0,3} // attacklab: g_tab_width - 1 + ([*+-]|\d+[.]) // $3 = first list item marker + [ \t]+ + ) + [^\r]+? + ( // $4 + ~0 // sentinel for workaround; should be $ + | + \n{2,} + (?=\S) + (?! // Negative lookahead for another list item marker + [ \t]* + (?:[*+-]|\d+[.])[ \t]+ + ) + ) + ) + /g + */ + var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; + + if (g_list_level) { + text = text.replace(whole_list, function (wholeMatch, m1, m2) { + var list = m1; + var list_type = (m2.search(/[*+-]/g) > -1) ? "ul" : "ol"; + + var result = _ProcessListItems(list, list_type); + + // Trim any trailing whitespace, to put the closing `` + // up on the preceding line, to get it past the current stupid + // HTML block parser. This is a hack to work around the terrible + // hack that is the HTML block parser. + result = result.replace(/\s+$/, ""); + result = "<" + list_type + ">" + result + "\n"; + return result; + }); + } else { + whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g; + text = text.replace(whole_list, function (wholeMatch, m1, m2, m3) { + var runup = m1; + var list = m2; + + var list_type = (m3.search(/[*+-]/g) > -1) ? "ul" : "ol"; + var result = _ProcessListItems(list, list_type); + result = runup + "<" + list_type + ">\n" + result + "\n"; + return result; + }); + } + + // attacklab: strip sentinel + text = text.replace(/~0/, ""); + + return text; + } + + var _listItemMarkers = { ol: "\\d+[.]", ul: "[*+-]" }; + + function _ProcessListItems(list_str, list_type) { + // + // Process the contents of a single ordered or unordered list, splitting it + // into individual list items. + // + // list_type is either "ul" or "ol". + + // The $g_list_level global keeps track of when we're inside a list. + // Each time we enter a list, we increment it; when we leave a list, + // we decrement. If it's zero, we're not in a list anymore. + // + // We do this because when we're not inside a list, we want to treat + // something like this: + // + // I recommend upgrading to version + // 8. Oops, now this line is treated + // as a sub-list. + // + // As a single paragraph, despite the fact that the second line starts + // with a digit-period-space sequence. + // + // Whereas when we're inside a list (or sub-list), that line will be + // treated as the start of a sub-list. What a kludge, huh? This is + // an aspect of Markdown's syntax that's hard to parse perfectly + // without resorting to mind-reading. Perhaps the solution is to + // change the syntax rules such that sub-lists must start with a + // starting cardinal number; e.g. "1." or "a.". + + g_list_level++; + + // trim trailing blank lines: + list_str = list_str.replace(/\n{2,}$/, "\n"); + + // attacklab: add sentinel to emulate \z + list_str += "~0"; + + // In the original attacklab showdown, list_type was not given to this function, and anything + // that matched /[*+-]|\d+[.]/ would just create the next
  • , causing this mismatch: + // + // Markdown rendered by WMD rendered by MarkdownSharp + // ------------------------------------------------------------------ + // 1. first 1. first 1. first + // 2. second 2. second 2. second + // - third 3. third * third + // + // We changed this to behave identical to MarkdownSharp. This is the constructed RegEx, + // with {MARKER} being one of \d+[.] or [*+-], depending on list_type: + + /* + list_str = list_str.replace(/ + (^[ \t]*) // leading whitespace = $1 + ({MARKER}) [ \t]+ // list marker = $2 + ([^\r]+? // list item text = $3 + (\n+) + ) + (?= + (~0 | \2 ({MARKER}) [ \t]+) + ) + /gm, function(){...}); + */ + + var marker = _listItemMarkers[list_type]; + var re = new RegExp("(^[ \\t]*)(" + marker + ")[ \\t]+([^\\r]+?(\\n+))(?=(~0|\\1(" + marker + ")[ \\t]+))", "gm"); + var last_item_had_a_double_newline = false; + list_str = list_str.replace(re, + function (wholeMatch, m1, m2, m3) { + var item = m3; + var leading_space = m1; + var ends_with_double_newline = /\n\n$/.test(item); + var contains_double_newline = ends_with_double_newline || item.search(/\n{2,}/) > -1; + + if (contains_double_newline || last_item_had_a_double_newline) { + item = _RunBlockGamut(_Outdent(item), /* doNotUnhash = */true); + } + else { + // Recursion for sub-lists: + item = _DoLists(_Outdent(item)); + item = item.replace(/\n$/, ""); // chomp(item) + item = _RunSpanGamut(item); + } + last_item_had_a_double_newline = ends_with_double_newline; + return "
  • " + item + "
  • \n"; + } + ); + + // attacklab: strip sentinel + list_str = list_str.replace(/~0/g, ""); + + g_list_level--; + return list_str; + } + + function _DoCodeBlocks(text) { + // + // Process Markdown `
    ` blocks.
    +			//  
    +
    +			/*
    +            text = text.replace(/
    +                (?:\n\n|^)
    +                (                               // $1 = the code block -- one or more lines, starting with a space/tab
    +                    (?:
    +                        (?:[ ]{4}|\t)           // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
    +                        .*\n+
    +                    )+
    +                )
    +                (\n*[ ]{0,3}[^ \t\n]|(?=~0))    // attacklab: g_tab_width
    +            /g ,function(){...});
    +            */
    +
    +			// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
    +			text += "~0";
    +
    +			text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
    +                function (wholeMatch, m1, m2) {
    +                	var codeblock = m1;
    +                	var nextChar = m2;
    +
    +                	codeblock = _EncodeCode(_Outdent(codeblock));
    +                	codeblock = _Detab(codeblock);
    +                	codeblock = codeblock.replace(/^\n+/g, ""); // trim leading newlines
    +                	codeblock = codeblock.replace(/\n+$/g, ""); // trim trailing whitespace
    +
    +                	codeblock = '
    ' + codeblock + '\n
    '; + + return "\n\n" + codeblock + "\n\n" + nextChar; + } + ); + + // attacklab: strip sentinel + text = text.replace(/~0/, ""); + + return text; + } + + function hashBlock(text) { + text = text.replace(/(^\n+|\n+$)/g, ""); + return "\n\n~K" + (g_html_blocks.push(text) - 1) + "K\n\n"; + } + + function _DoCodeSpans(text) { + // + // * Backtick quotes are used for spans. + // + // * You can use multiple backticks as the delimiters if you want to + // include literal backticks in the code span. So, this input: + // + // Just type ``foo `bar` baz`` at the prompt. + // + // Will translate to: + // + //

    Just type foo `bar` baz at the prompt.

    + // + // There's no arbitrary limit to the number of backticks you + // can use as delimters. If you need three consecutive backticks + // in your code, use four for delimiters, etc. + // + // * You can use spaces to get literal backticks at the edges: + // + // ... type `` `bar` `` ... + // + // Turns to: + // + // ... type `bar` ... + // + + /* + text = text.replace(/ + (^|[^\\]) // Character before opening ` can't be a backslash + (`+) // $2 = Opening run of ` + ( // $3 = The code block + [^\r]*? + [^`] // attacklab: work around lack of lookbehind + ) + \2 // Matching closer + (?!`) + /gm, function(){...}); + */ + + text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, + function (wholeMatch, m1, m2, m3, m4) { + var c = m3; + c = c.replace(/^([ \t]*)/g, ""); // leading whitespace + c = c.replace(/[ \t]*$/g, ""); // trailing whitespace + c = _EncodeCode(c); + c = c.replace(/:\/\//g, "~P"); // to prevent auto-linking. Not necessary in code *blocks*, but in code spans. Will be converted back after the auto-linker runs. + return m1 + "" + c + ""; + } + ); + + return text; + } + + function _EncodeCode(text) { + // + // Encode/escape certain characters inside Markdown code runs. + // The point is that in code, these characters are literals, + // and lose their special Markdown meanings. + // + // Encode all ampersands; HTML entities are not + // entities within a Markdown code span. + text = text.replace(/&/g, "&"); + + // Do the angle bracket song and dance: + text = text.replace(//g, ">"); + + // Now, escape characters that are magic in Markdown: + text = escapeCharacters(text, "\*_{}[]\\", false); + + // jj the line above breaks this: + //--- + + //* Item + + // 1. Subitem + + // special char: * + //--- + + return text; + } + + function _DoItalicsAndBold(text) { + + // must go first: + text = text.replace(/([\W_]|^)(\*\*|__)(?=\S)([^\r]*?\S[\*_]*)\2([\W_]|$)/g, + "$1$3$4"); + + text = text.replace(/([\W_]|^)(\*|_)(?=\S)([^\r\*_]*?\S)\2([\W_]|$)/g, + "$1$3$4"); + + return text; + } + + function _DoBlockQuotes(text) { + + /* + text = text.replace(/ + ( // Wrap whole match in $1 + ( + ^[ \t]*>[ \t]? // '>' at the start of a line + .+\n // rest of the first line + (.+\n)* // subsequent consecutive lines + \n* // blanks + )+ + ) + /gm, function(){...}); + */ + + text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm, + function (wholeMatch, m1) { + var bq = m1; + + // attacklab: hack around Konqueror 3.5.4 bug: + // "----------bug".replace(/^-/g,"") == "bug" + + bq = bq.replace(/^[ \t]*>[ \t]?/gm, "~0"); // trim one level of quoting + + // attacklab: clean up hack + bq = bq.replace(/~0/g, ""); + + bq = bq.replace(/^[ \t]+$/gm, ""); // trim whitespace-only lines + bq = _RunBlockGamut(bq); // recurse + + bq = bq.replace(/(^|\n)/g, "$1 "); + // These leading spaces screw with
     content, so we need to fix that:
    +                	bq = bq.replace(
    +                            /(\s*
    [^\r]+?<\/pre>)/gm,
    +                        function (wholeMatch, m1) {
    +                        	var pre = m1;
    +                        	// attacklab: hack around Konqueror 3.5.4 bug:
    +                        	pre = pre.replace(/^  /mg, "~0");
    +                        	pre = pre.replace(/~0/g, "");
    +                        	return pre;
    +                        });
    +
    +                	return hashBlock("
    \n" + bq + "\n
    "); + } + ); + return text; + } + + function _FormParagraphs(text, doNotUnhash) { + // + // Params: + // $text - string to process with html

    tags + // + + // Strip leading and trailing lines: + text = text.replace(/^\n+/g, ""); + text = text.replace(/\n+$/g, ""); + + var grafs = text.split(/\n{2,}/g); + var grafsOut = []; + + var markerRe = /~K(\d+)K/; + + // + // Wrap

    tags. + // + var end = grafs.length; + for (var i = 0; i < end; i++) { + var str = grafs[i]; + + // if this is an HTML marker, copy it + if (markerRe.test(str)) { + grafsOut.push(str); + } + else if (/\S/.test(str)) { + str = _RunSpanGamut(str); + str = str.replace(/^([ \t]*)/g, "

    "); + str += "

    " + grafsOut.push(str); + } + + } + // + // Unhashify HTML blocks + // + if (!doNotUnhash) { + end = grafsOut.length; + for (var i = 0; i < end; i++) { + var foundAny = true; + while (foundAny) { // we may need several runs, since the data may be nested + foundAny = false; + grafsOut[i] = grafsOut[i].replace(/~K(\d+)K/g, function (wholeMatch, id) { + foundAny = true; + return g_html_blocks[id]; + }); + } + } + } + return grafsOut.join("\n\n"); + } + + function _EncodeAmpsAndAngles(text) { + // Smart processing for ampersands and angle brackets that need to be encoded. + + // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: + // http://bumppo.net/projects/amputator/ + text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, "&"); + + // Encode naked <'s + text = text.replace(/<(?![a-z\/?\$!])/gi, "<"); + + return text; + } + + function _EncodeBackslashEscapes(text) { + // + // Parameter: String. + // Returns: The string, with after processing the following backslash + // escape sequences. + // + + // attacklab: The polite way to do this is with the new + // escapeCharacters() function: + // + // text = escapeCharacters(text,"\\",true); + // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true); + // + // ...but we're sidestepping its use of the (slow) RegExp constructor + // as an optimization for Firefox. This function gets called a LOT. + + text = text.replace(/\\(\\)/g, escapeCharacters_callback); + text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, escapeCharacters_callback); + return text; + } + + function _DoAutoLinks(text) { + + // note that at this point, all other URL in the text are already hyperlinked as
    + // *except* for the case + + // automatically add < and > around unadorned raw hyperlinks + // must be preceded by space/BOF and followed by non-word/EOF character + text = text.replace(/(^|\s)(https?|ftp)(:\/\/[-A-Z0-9+&@#\/%?=~_|\[\]\(\)!:,\.;]*[-A-Z0-9+&@#\/%=~_|\[\]])($|\W)/gi, "$1<$2$3>$4"); + + // autolink anything like + + var replacer = function (wholematch, m1) { return "" + pluginHooks.plainLinkText(m1) + ""; } + text = text.replace(/<((https?|ftp):[^'">\s]+)>/gi, replacer); + + // Email addresses: + /* + text = text.replace(/ + < + (?:mailto:)? + ( + [-.\w]+ + \@ + [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ + ) + > + /gi, _DoAutoLinks_callback()); + */ + + var email_replacer = function (wholematch, m1) { + var mailto = 'mailto:' + var link + var email + if (m1.substring(0, mailto.length) != mailto) { + link = mailto + m1; + email = m1; + } else { + link = m1; + email = m1.substring(mailto.length, m1.length); + } + return "" + pluginHooks.plainLinkText(email) + ""; + } + text = text.replace(/<((?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+))>/gi, email_replacer); + + return text; + } + + function _UnescapeSpecialChars(text) { + // + // Swap back in all the special characters we've hidden. + // + text = text.replace(/~E(\d+)E/g, + function (wholeMatch, m1) { + var charCodeToReplace = parseInt(m1); + return String.fromCharCode(charCodeToReplace); + } + ); + return text; + } + + function _Outdent(text) { + // + // Remove one level of line-leading tabs or spaces + // + + // attacklab: hack around Konqueror 3.5.4 bug: + // "----------bug".replace(/^-/g,"") == "bug" + + text = text.replace(/^(\t|[ ]{1,4})/gm, "~0"); // attacklab: g_tab_width + + // attacklab: clean up hack + text = text.replace(/~0/g, "") + + return text; + } + + function _Detab(text) { + if (!/\t/.test(text)) + return text; + + var spaces = [" ", " ", " ", " "], + skew = 0, + v; + + return text.replace(/[\n\t]/g, function (match, offset) { + if (match === "\n") { + skew = offset + 1; + return match; + } + v = (offset - skew) % 4; + skew = offset + 1; + return spaces[v]; + }); + } + + // + // attacklab: Utility functions + // + + var _problemUrlChars = /(?:["'*()[\]:]|~D)/g; + + // hex-encodes some unusual "problem" chars in URLs to avoid URL detection problems + function encodeProblemUrlChars(url) { + if (!url) + return ""; + + var len = url.length; + + return url.replace(_problemUrlChars, function (match, offset) { + if (match == "~D") // escape for dollar + return "%24"; + if (match == ":") { + if (offset == len - 1 || /[0-9\/]/.test(url.charAt(offset + 1))) + return ":"; + if (url.substring(0, 'mailto:'.length) === 'mailto:') + return ":"; + if (url.substring(0, 'magnet:'.length) === 'magnet:') + return ":"; + } + return "%" + match.charCodeAt(0).toString(16); + }); + } + + + function escapeCharacters(text, charsToEscape, afterBackslash) { + // First we have to escape the escape characters so that + // we can build a character class out of them + var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g, "\\$1") + "])"; + + if (afterBackslash) { + regexString = "\\\\" + regexString; + } + + var regex = new RegExp(regexString, "g"); + text = text.replace(regex, escapeCharacters_callback); + + return text; + } + + + function escapeCharacters_callback(wholeMatch, m1) { + var charCodeToEscape = m1.charCodeAt(0); + return "~E" + charCodeToEscape + "E"; + } + + }; // end of the Markdown.Converter constructor + })(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/markdown/markdown.css b/src/Umbraco.Web.UI.Client/lib/markdown/markdown.css index 45c9b39c48..a9e53c65a0 100644 --- a/src/Umbraco.Web.UI.Client/lib/markdown/markdown.css +++ b/src/Umbraco.Web.UI.Client/lib/markdown/markdown.css @@ -1,80 +1,80 @@ -.wmd-panel { - width: 100%; -} - -.wmd-input { - height: 300px; - width: 100%; - box-sizing: border-box; - -webkit-box-sizing:border-box; - -moz-box-sizing: border-box; - -ms-box-sizing: border-box; -} - -.wmd-preview { - .well; - width: 100%; - box-sizing: border-box; - -webkit-box-sizing:border-box; - -moz-box-sizing: border-box; - -ms-box-sizing: border-box; -} - -.wmd-panel .btn-toolbar { - margin-bottom: 0; - padding: 0; - width: 100%; -} - -/* -.icon-link, -.icon-blockquote, -.icon-code, -.icon-bullet-list, -.icon-list, -.icon-header, -.icon-hr-line, -.icon-undo { - background-image: url(Markdown.Editor.Icons.png); -} -.icon-link { background-position: 0 0; } -.icon-blockquote { background-position: -24px 0; } -.icon-code { background-position: -48px 0; } -.icon-bullet-list { background-position: -72px 0; } -.icon-list { background-position: -96px 0; } -.icon-header { background-position: -120px 0; } -.icon-hr-line { background-position: -144px 0; } -.icon-undo { background-position: -168px 0; } - */ - - - - -.wmd-prompt-background -{ - background-color: Black; -} - -.wmd-prompt-dialog -{ - border: 1px solid #999999; - background-color: #F5F5F5; -} - -.wmd-prompt-dialog > div { - font-size: 0.8em; - font-family: arial, helvetica, sans-serif; -} - - -.wmd-prompt-dialog > form > input[type="text"] { - border: 1px solid #999999; - color: black; -} - -.wmd-prompt-dialog > form > input[type="button"]{ - border: 1px solid #888888; - font-family: trebuchet MS, helvetica, sans-serif; - font-size: 0.8em; - font-weight: bold; -} +.wmd-panel { + width: 100%; +} + +.wmd-input { + height: 300px; + width: 100%; + box-sizing: border-box; + -webkit-box-sizing:border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; +} + +.wmd-preview { + .well; + width: 100%; + box-sizing: border-box; + -webkit-box-sizing:border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; +} + +.wmd-panel .btn-toolbar { + margin-bottom: 0; + padding: 0; + width: 100%; +} + +/* +.icon-link, +.icon-blockquote, +.icon-code, +.icon-bullet-list, +.icon-list, +.icon-header, +.icon-hr-line, +.icon-undo { + background-image: url(Markdown.Editor.Icons.png); +} +.icon-link { background-position: 0 0; } +.icon-blockquote { background-position: -24px 0; } +.icon-code { background-position: -48px 0; } +.icon-bullet-list { background-position: -72px 0; } +.icon-list { background-position: -96px 0; } +.icon-header { background-position: -120px 0; } +.icon-hr-line { background-position: -144px 0; } +.icon-undo { background-position: -168px 0; } + */ + + + + +.wmd-prompt-background +{ + background-color: Black; +} + +.wmd-prompt-dialog +{ + border: 1px solid #999999; + background-color: #F5F5F5; +} + +.wmd-prompt-dialog > div { + font-size: 0.8em; + font-family: arial, helvetica, sans-serif; +} + + +.wmd-prompt-dialog > form > input[type="text"] { + border: 1px solid #999999; + color: black; +} + +.wmd-prompt-dialog > form > input[type="button"]{ + border: 1px solid #888888; + font-family: trebuchet MS, helvetica, sans-serif; + font-size: 0.8em; + font-weight: bold; +} diff --git a/src/Umbraco.Web.UI.Client/lib/markdown/markdown.editor.js b/src/Umbraco.Web.UI.Client/lib/markdown/markdown.editor.js index 21ad5d0a8b..14029bfaf6 100644 --- a/src/Umbraco.Web.UI.Client/lib/markdown/markdown.editor.js +++ b/src/Umbraco.Web.UI.Client/lib/markdown/markdown.editor.js @@ -1,2110 +1,2110 @@ -// needs Markdown.Converter.js at the moment - -(function () { - - var util = {}, - position = {}, - ui = {}, - doc = window.document, - re = window.RegExp, - nav = window.navigator, - SETTINGS = { lineLength: 72 }, - - // Used to work around some browser bugs where we can't use feature testing. - uaSniffed = { - isIE: /msie/.test(nav.userAgent.toLowerCase()), - isIE_5or6: /msie 6/.test(nav.userAgent.toLowerCase()) || /msie 5/.test(nav.userAgent.toLowerCase()), - isOpera: /opera/.test(nav.userAgent.toLowerCase()) - }; - - - // ------------------------------------------------------------------- - // YOUR CHANGES GO HERE - // - // I've tried to localize the things you are likely to change to - // this area. - // ------------------------------------------------------------------- - - // The text that appears on the upper part of the dialog box when - // entering links. - var linkDialogText = "

    http://example.com/ \"optional title\"

    "; - var imageDialogText = "

    http://example.com/images/diagram.jpg \"optional title\"

    "; - - // The default text that appears in the dialog input box when entering - // links. - var imageDefaultText = "http://"; - var linkDefaultText = "http://"; - - var defaultHelpHoverTitle = "Markdown Editing Help"; - - // ------------------------------------------------------------------- - // END OF YOUR CHANGES - // ------------------------------------------------------------------- - - // help, if given, should have a property "handler", the click handler for the help button, - // and can have an optional property "title" for the button's tooltip (defaults to "Markdown Editing Help"). - // If help isn't given, not help button is created. - // - // The constructed editor object has the methods: - // - getConverter() returns the markdown converter object that was passed to the constructor - // - run() actually starts the editor; should be called after all necessary plugins are registered. Calling this more than once is a no-op. - // - refreshPreview() forces the preview to be updated. This method is only available after run() was called. - Markdown.Editor = function (markdownConverter, idPostfix, help) { - - idPostfix = idPostfix || ""; - - var hooks = this.hooks = new Markdown.HookCollection(); - hooks.addNoop("onPreviewRefresh"); // called with no arguments after the preview has been refreshed - hooks.addNoop("postBlockquoteCreation"); // called with the user's selection *after* the blockquote was created; should return the actual to-be-inserted text - hooks.addFalse("insertImageDialog"); /* called with one parameter: a callback to be called with the URL of the image. If the application creates - * its own image insertion dialog, this hook should return true, and the callback should be called with the chosen - * image url (or null if the user cancelled). If this hook returns false, the default dialog will be used. - */ - - this.getConverter = function () { return markdownConverter; } - - var that = this, - panels; - - this.run = function () { - if (panels) - return; // already initialized - - panels = new PanelCollection(idPostfix); - var commandManager = new CommandManager(hooks); - var previewManager = new PreviewManager(markdownConverter, panels, function () { hooks.onPreviewRefresh(); }); - var undoManager, uiManager; - - if (!/\?noundo/.test(doc.location.href)) { - undoManager = new UndoManager(function () { - previewManager.refresh(); - if (uiManager) // not available on the first call - uiManager.setUndoRedoButtonStates(); - }, panels); - this.textOperation = function (f) { - undoManager.setCommandMode(); - f(); - that.refreshPreview(); - } - } - - uiManager = new UIManager(idPostfix, panels, undoManager, previewManager, commandManager, help); - uiManager.setUndoRedoButtonStates(); - - var forceRefresh = that.refreshPreview = function () { previewManager.refresh(true); }; - - forceRefresh(); - }; - - } - - // before: contains all the text in the input box BEFORE the selection. - // after: contains all the text in the input box AFTER the selection. - function Chunks() { } - - // startRegex: a regular expression to find the start tag - // endRegex: a regular expresssion to find the end tag - Chunks.prototype.findTags = function (startRegex, endRegex) { - - var chunkObj = this; - var regex; - - if (startRegex) { - - regex = util.extendRegExp(startRegex, "", "$"); - - this.before = this.before.replace(regex, - function (match) { - chunkObj.startTag = chunkObj.startTag + match; - return ""; - }); - - regex = util.extendRegExp(startRegex, "^", ""); - - this.selection = this.selection.replace(regex, - function (match) { - chunkObj.startTag = chunkObj.startTag + match; - return ""; - }); - } - - if (endRegex) { - - regex = util.extendRegExp(endRegex, "", "$"); - - this.selection = this.selection.replace(regex, - function (match) { - chunkObj.endTag = match + chunkObj.endTag; - return ""; - }); - - regex = util.extendRegExp(endRegex, "^", ""); - - this.after = this.after.replace(regex, - function (match) { - chunkObj.endTag = match + chunkObj.endTag; - return ""; - }); - } - }; - - // If remove is false, the whitespace is transferred - // to the before/after regions. - // - // If remove is true, the whitespace disappears. - Chunks.prototype.trimWhitespace = function (remove) { - var beforeReplacer, afterReplacer, that = this; - if (remove) { - beforeReplacer = afterReplacer = ""; - } else { - beforeReplacer = function (s) { that.before += s; return ""; } - afterReplacer = function (s) { that.after = s + that.after; return ""; } - } - - this.selection = this.selection.replace(/^(\s*)/, beforeReplacer).replace(/(\s*)$/, afterReplacer); - }; - - - Chunks.prototype.skipLines = function (nLinesBefore, nLinesAfter, findExtraNewlines) { - - if (nLinesBefore === undefined) { - nLinesBefore = 1; - } - - if (nLinesAfter === undefined) { - nLinesAfter = 1; - } - - nLinesBefore++; - nLinesAfter++; - - var regexText; - var replacementText; - - // chrome bug ... documented at: http://meta.stackoverflow.com/questions/63307/blockquote-glitch-in-editor-in-chrome-6-and-7/65985#65985 - if (navigator.userAgent.match(/Chrome/)) { - "X".match(/()./); - } - - this.selection = this.selection.replace(/(^\n*)/, ""); - - this.startTag = this.startTag + re.$1; - - this.selection = this.selection.replace(/(\n*$)/, ""); - this.endTag = this.endTag + re.$1; - this.startTag = this.startTag.replace(/(^\n*)/, ""); - this.before = this.before + re.$1; - this.endTag = this.endTag.replace(/(\n*$)/, ""); - this.after = this.after + re.$1; - - if (this.before) { - - regexText = replacementText = ""; - - while (nLinesBefore--) { - regexText += "\\n?"; - replacementText += "\n"; - } - - if (findExtraNewlines) { - regexText = "\\n*"; - } - this.before = this.before.replace(new re(regexText + "$", ""), replacementText); - } - - if (this.after) { - - regexText = replacementText = ""; - - while (nLinesAfter--) { - regexText += "\\n?"; - replacementText += "\n"; - } - if (findExtraNewlines) { - regexText = "\\n*"; - } - - this.after = this.after.replace(new re(regexText, ""), replacementText); - } - }; - - // end of Chunks - - // A collection of the important regions on the page. - // Cached so we don't have to keep traversing the DOM. - // Also holds ieCachedRange and ieCachedScrollTop, where necessary; working around - // this issue: - // Internet explorer has problems with CSS sprite buttons that use HTML - // lists. When you click on the background image "button", IE will - // select the non-existent link text and discard the selection in the - // textarea. The solution to this is to cache the textarea selection - // on the button's mousedown event and set a flag. In the part of the - // code where we need to grab the selection, we check for the flag - // and, if it's set, use the cached area instead of querying the - // textarea. - // - // This ONLY affects Internet Explorer (tested on versions 6, 7 - // and 8) and ONLY on button clicks. Keyboard shortcuts work - // normally since the focus never leaves the textarea. - function PanelCollection(postfix) { - this.buttonBar = doc.getElementById("wmd-button-bar" + postfix); - this.preview = doc.getElementById("wmd-preview" + postfix); - this.input = doc.getElementById("wmd-input" + postfix); - }; - - // Returns true if the DOM element is visible, false if it's hidden. - // Checks if display is anything other than none. - util.isVisible = function (elem) { - - if (window.getComputedStyle) { - // Most browsers - return window.getComputedStyle(elem, null).getPropertyValue("display") !== "none"; - } - else if (elem.currentStyle) { - // IE - return elem.currentStyle["display"] !== "none"; - } - }; - - - // Adds a listener callback to a DOM element which is fired on a specified - // event. - util.addEvent = function (elem, event, listener) { - if (elem.attachEvent) { - // IE only. The "on" is mandatory. - elem.attachEvent("on" + event, listener); - } - else { - // Other browsers. - elem.addEventListener(event, listener, false); - } - }; - - - // Removes a listener callback from a DOM element which is fired on a specified - // event. - util.removeEvent = function (elem, event, listener) { - if (elem.detachEvent) { - // IE only. The "on" is mandatory. - elem.detachEvent("on" + event, listener); - } - else { - // Other browsers. - elem.removeEventListener(event, listener, false); - } - }; - - // Converts \r\n and \r to \n. - util.fixEolChars = function (text) { - text = text.replace(/\r\n/g, "\n"); - text = text.replace(/\r/g, "\n"); - return text; - }; - - // Extends a regular expression. Returns a new RegExp - // using pre + regex + post as the expression. - // Used in a few functions where we have a base - // expression and we want to pre- or append some - // conditions to it (e.g. adding "$" to the end). - // The flags are unchanged. - // - // regex is a RegExp, pre and post are strings. - util.extendRegExp = function (regex, pre, post) { - - if (pre === null || pre === undefined) { - pre = ""; - } - if (post === null || post === undefined) { - post = ""; - } - - var pattern = regex.toString(); - var flags; - - // Replace the flags with empty space and store them. - pattern = pattern.replace(/\/([gim]*)$/, function (wholeMatch, flagsPart) { - flags = flagsPart; - return ""; - }); - - // Remove the slash delimiters on the regular expression. - pattern = pattern.replace(/(^\/|\/$)/g, ""); - pattern = pre + pattern + post; - - return new re(pattern, flags); - } - - // UNFINISHED - // The assignment in the while loop makes jslint cranky. - // I'll change it to a better loop later. - position.getTop = function (elem, isInner) { - var result = elem.offsetTop; - if (!isInner) { - while (elem = elem.offsetParent) { - result += elem.offsetTop; - } - } - return result; - }; - - position.getHeight = function (elem) { - return elem.offsetHeight || elem.scrollHeight; - }; - - position.getWidth = function (elem) { - return elem.offsetWidth || elem.scrollWidth; - }; - - position.getPageSize = function () { - - var scrollWidth, scrollHeight; - var innerWidth, innerHeight; - - // It's not very clear which blocks work with which browsers. - if (self.innerHeight && self.scrollMaxY) { - scrollWidth = doc.body.scrollWidth; - scrollHeight = self.innerHeight + self.scrollMaxY; - } - else if (doc.body.scrollHeight > doc.body.offsetHeight) { - scrollWidth = doc.body.scrollWidth; - scrollHeight = doc.body.scrollHeight; - } - else { - scrollWidth = doc.body.offsetWidth; - scrollHeight = doc.body.offsetHeight; - } - - if (self.innerHeight) { - // Non-IE browser - innerWidth = self.innerWidth; - innerHeight = self.innerHeight; - } - else if (doc.documentElement && doc.documentElement.clientHeight) { - // Some versions of IE (IE 6 w/ a DOCTYPE declaration) - innerWidth = doc.documentElement.clientWidth; - innerHeight = doc.documentElement.clientHeight; - } - else if (doc.body) { - // Other versions of IE - innerWidth = doc.body.clientWidth; - innerHeight = doc.body.clientHeight; - } - - var maxWidth = Math.max(scrollWidth, innerWidth); - var maxHeight = Math.max(scrollHeight, innerHeight); - return [maxWidth, maxHeight, innerWidth, innerHeight]; - }; - - // Handles pushing and popping TextareaStates for undo/redo commands. - // I should rename the stack variables to list. - function UndoManager(callback, panels) { - - var undoObj = this; - var undoStack = []; // A stack of undo states - var stackPtr = 0; // The index of the current state - var mode = "none"; - var lastState; // The last state - var timer; // The setTimeout handle for cancelling the timer - var inputStateObj; - - // Set the mode for later logic steps. - var setMode = function (newMode, noSave) { - if (mode != newMode) { - mode = newMode; - if (!noSave) { - saveState(); - } - } - - if (!uaSniffed.isIE || mode != "moving") { - timer = setTimeout(refreshState, 1); - } - else { - inputStateObj = null; - } - }; - - var refreshState = function (isInitialState) { - inputStateObj = new TextareaState(panels, isInitialState); - timer = undefined; - }; - - this.setCommandMode = function () { - mode = "command"; - saveState(); - timer = setTimeout(refreshState, 0); - }; - - this.canUndo = function () { - return stackPtr > 1; - }; - - this.canRedo = function () { - if (undoStack[stackPtr + 1]) { - return true; - } - return false; - }; - - // Removes the last state and restores it. - this.undo = function () { - - if (undoObj.canUndo()) { - if (lastState) { - // What about setting state -1 to null or checking for undefined? - lastState.restore(); - lastState = null; - } - else { - undoStack[stackPtr] = new TextareaState(panels); - undoStack[--stackPtr].restore(); - - if (callback) { - callback(); - } - } - } - - mode = "none"; - panels.input.focus(); - refreshState(); - }; - - // Redo an action. - this.redo = function () { - - if (undoObj.canRedo()) { - - undoStack[++stackPtr].restore(); - - if (callback) { - callback(); - } - } - - mode = "none"; - panels.input.focus(); - refreshState(); - }; - - // Push the input area state to the stack. - var saveState = function () { - var currState = inputStateObj || new TextareaState(panels); - - if (!currState) { - return false; - } - if (mode == "moving") { - if (!lastState) { - lastState = currState; - } - return; - } - if (lastState) { - if (undoStack[stackPtr - 1].text != lastState.text) { - undoStack[stackPtr++] = lastState; - } - lastState = null; - } - undoStack[stackPtr++] = currState; - undoStack[stackPtr + 1] = null; - if (callback) { - callback(); - } - }; - - var handleCtrlYZ = function (event) { - - var handled = false; - - if (event.ctrlKey || event.metaKey) { - - // IE and Opera do not support charCode. - var keyCode = event.charCode || event.keyCode; - var keyCodeChar = String.fromCharCode(keyCode); - - switch (keyCodeChar) { - - case "y": - undoObj.redo(); - handled = true; - break; - - case "z": - if (!event.shiftKey) { - undoObj.undo(); - } - else { - undoObj.redo(); - } - handled = true; - break; - } - } - - if (handled) { - if (event.preventDefault) { - event.preventDefault(); - } - if (window.event) { - window.event.returnValue = false; - } - return; - } - }; - - // Set the mode depending on what is going on in the input area. - var handleModeChange = function (event) { - - if (!event.ctrlKey && !event.metaKey) { - - var keyCode = event.keyCode; - - if ((keyCode >= 33 && keyCode <= 40) || (keyCode >= 63232 && keyCode <= 63235)) { - // 33 - 40: page up/dn and arrow keys - // 63232 - 63235: page up/dn and arrow keys on safari - setMode("moving"); - } - else if (keyCode == 8 || keyCode == 46 || keyCode == 127) { - // 8: backspace - // 46: delete - // 127: delete - setMode("deleting"); - } - else if (keyCode == 13) { - // 13: Enter - setMode("newlines"); - } - else if (keyCode == 27) { - // 27: escape - setMode("escape"); - } - else if ((keyCode < 16 || keyCode > 20) && keyCode != 91) { - // 16-20 are shift, etc. - // 91: left window key - // I think this might be a little messed up since there are - // a lot of nonprinting keys above 20. - setMode("typing"); - } - } - }; - - var setEventHandlers = function () { - util.addEvent(panels.input, "keypress", function (event) { - // keyCode 89: y - // keyCode 90: z - if ((event.ctrlKey || event.metaKey) && (event.keyCode == 89 || event.keyCode == 90)) { - event.preventDefault(); - } - }); - - var handlePaste = function () { - if (uaSniffed.isIE || (inputStateObj && inputStateObj.text != panels.input.value)) { - if (timer == undefined) { - mode = "paste"; - saveState(); - refreshState(); - } - } - }; - - util.addEvent(panels.input, "keydown", handleCtrlYZ); - util.addEvent(panels.input, "keydown", handleModeChange); - util.addEvent(panels.input, "mousedown", function () { - setMode("moving"); - }); - - panels.input.onpaste = handlePaste; - panels.input.ondrop = handlePaste; - }; - - var init = function () { - setEventHandlers(); - refreshState(true); - saveState(); - }; - - init(); - } - - // end of UndoManager - - // The input textarea state/contents. - // This is used to implement undo/redo by the undo manager. - function TextareaState(panels, isInitialState) { - - // Aliases - var stateObj = this; - var inputArea = panels.input; - this.init = function () { - if (!util.isVisible(inputArea)) { - return; - } - if (!isInitialState && doc.activeElement && doc.activeElement !== inputArea) { // this happens when tabbing out of the input box - return; - } - - this.setInputAreaSelectionStartEnd(); - this.scrollTop = inputArea.scrollTop; - if (!this.text && inputArea.selectionStart || inputArea.selectionStart === 0) { - this.text = inputArea.value; - } - - } - - // Sets the selected text in the input box after we've performed an - // operation. - this.setInputAreaSelection = function () { - - if (!util.isVisible(inputArea)) { - return; - } - - if (inputArea.selectionStart !== undefined && !uaSniffed.isOpera) { - - inputArea.focus(); - inputArea.selectionStart = stateObj.start; - inputArea.selectionEnd = stateObj.end; - inputArea.scrollTop = stateObj.scrollTop; - } - else if (doc.selection) { - - if (doc.activeElement && doc.activeElement !== inputArea) { - return; - } - - inputArea.focus(); - var range = inputArea.createTextRange(); - range.moveStart("character", -inputArea.value.length); - range.moveEnd("character", -inputArea.value.length); - range.moveEnd("character", stateObj.end); - range.moveStart("character", stateObj.start); - range.select(); - } - }; - - this.setInputAreaSelectionStartEnd = function () { - - if (!panels.ieCachedRange && (inputArea.selectionStart || inputArea.selectionStart === 0)) { - - stateObj.start = inputArea.selectionStart; - stateObj.end = inputArea.selectionEnd; - } - else if (doc.selection) { - - stateObj.text = util.fixEolChars(inputArea.value); - - // IE loses the selection in the textarea when buttons are - // clicked. On IE we cache the selection. Here, if something is cached, - // we take it. - var range = panels.ieCachedRange || doc.selection.createRange(); - - var fixedRange = util.fixEolChars(range.text); - var marker = "\x07"; - var markedRange = marker + fixedRange + marker; - range.text = markedRange; - var inputText = util.fixEolChars(inputArea.value); - - range.moveStart("character", -markedRange.length); - range.text = fixedRange; - - stateObj.start = inputText.indexOf(marker); - stateObj.end = inputText.lastIndexOf(marker) - marker.length; - - var len = stateObj.text.length - util.fixEolChars(inputArea.value).length; - - if (len) { - range.moveStart("character", -fixedRange.length); - while (len--) { - fixedRange += "\n"; - stateObj.end += 1; - } - range.text = fixedRange; - } - - if (panels.ieCachedRange) - stateObj.scrollTop = panels.ieCachedScrollTop; // this is set alongside with ieCachedRange - - panels.ieCachedRange = null; - - this.setInputAreaSelection(); - } - }; - - // Restore this state into the input area. - this.restore = function () { - - if (stateObj.text != undefined && stateObj.text != inputArea.value) { - inputArea.value = stateObj.text; - } - this.setInputAreaSelection(); - inputArea.scrollTop = stateObj.scrollTop; - }; - - // Gets a collection of HTML chunks from the inptut textarea. - this.getChunks = function () { - - var chunk = new Chunks(); - chunk.before = util.fixEolChars(stateObj.text.substring(0, stateObj.start)); - chunk.startTag = ""; - chunk.selection = util.fixEolChars(stateObj.text.substring(stateObj.start, stateObj.end)); - chunk.endTag = ""; - chunk.after = util.fixEolChars(stateObj.text.substring(stateObj.end)); - chunk.scrollTop = stateObj.scrollTop; - - return chunk; - }; - - // Sets the TextareaState properties given a chunk of markdown. - this.setChunks = function (chunk) { - - chunk.before = chunk.before + chunk.startTag; - chunk.after = chunk.endTag + chunk.after; - - this.start = chunk.before.length; - this.end = chunk.before.length + chunk.selection.length; - this.text = chunk.before + chunk.selection + chunk.after; - this.scrollTop = chunk.scrollTop; - }; - this.init(); - }; - - function PreviewManager(converter, panels, previewRefreshCallback) { - - var managerObj = this; - var timeout; - var elapsedTime; - var oldInputText; - var maxDelay = 3000; - var startType = "delayed"; // The other legal value is "manual" - - // Adds event listeners to elements - var setupEvents = function (inputElem, listener) { - - util.addEvent(inputElem, "input", listener); - inputElem.onpaste = listener; - inputElem.ondrop = listener; - - util.addEvent(inputElem, "keypress", listener); - util.addEvent(inputElem, "keydown", listener); - }; - - var getDocScrollTop = function () { - - var result = 0; - - if (window.innerHeight) { - result = window.pageYOffset; - } - else - if (doc.documentElement && doc.documentElement.scrollTop) { - result = doc.documentElement.scrollTop; - } - else - if (doc.body) { - result = doc.body.scrollTop; - } - - return result; - }; - - var makePreviewHtml = function () { - - // If there is no registered preview panel - // there is nothing to do. - if (!panels.preview) - return; - - - var text = panels.input.value; - if (text && text == oldInputText) { - return; // Input text hasn't changed. - } - else { - oldInputText = text; - } - - var prevTime = new Date().getTime(); - - text = converter.makeHtml(text); - - // Calculate the processing time of the HTML creation. - // It's used as the delay time in the event listener. - var currTime = new Date().getTime(); - elapsedTime = currTime - prevTime; - - pushPreviewHtml(text); - }; - - // setTimeout is already used. Used as an event listener. - var applyTimeout = function () { - - if (timeout) { - clearTimeout(timeout); - timeout = undefined; - } - - if (startType !== "manual") { - - var delay = 0; - - if (startType === "delayed") { - delay = elapsedTime; - } - - if (delay > maxDelay) { - delay = maxDelay; - } - timeout = setTimeout(makePreviewHtml, delay); - } - }; - - var getScaleFactor = function (panel) { - if (panel.scrollHeight <= panel.clientHeight) { - return 1; - } - return panel.scrollTop / (panel.scrollHeight - panel.clientHeight); - }; - - var setPanelScrollTops = function () { - if (panels.preview) { - panels.preview.scrollTop = (panels.preview.scrollHeight - panels.preview.clientHeight) * getScaleFactor(panels.preview); - } - }; - - this.refresh = function (requiresRefresh) { - - if (requiresRefresh) { - oldInputText = ""; - makePreviewHtml(); - } - else { - applyTimeout(); - } - }; - - this.processingTime = function () { - return elapsedTime; - }; - - var isFirstTimeFilled = true; - - // IE doesn't let you use innerHTML if the element is contained somewhere in a table - // (which is the case for inline editing) -- in that case, detach the element, set the - // value, and reattach. Yes, that *is* ridiculous. - var ieSafePreviewSet = function (text) { - var preview = panels.preview; - var parent = preview.parentNode; - var sibling = preview.nextSibling; - parent.removeChild(preview); - preview.innerHTML = text; - if (!sibling) - parent.appendChild(preview); - else - parent.insertBefore(preview, sibling); - } - - var nonSuckyBrowserPreviewSet = function (text) { - panels.preview.innerHTML = text; - } - - var previewSetter; - - var previewSet = function (text) { - if (previewSetter) - return previewSetter(text); - - try { - nonSuckyBrowserPreviewSet(text); - previewSetter = nonSuckyBrowserPreviewSet; - } catch (e) { - previewSetter = ieSafePreviewSet; - previewSetter(text); - } - }; - - var pushPreviewHtml = function (text) { - - var emptyTop = position.getTop(panels.input) - getDocScrollTop(); - - if (panels.preview) { - previewSet(text); - previewRefreshCallback(); - } - - setPanelScrollTops(); - - if (isFirstTimeFilled) { - isFirstTimeFilled = false; - return; - } - - var fullTop = position.getTop(panels.input) - getDocScrollTop(); - - if (uaSniffed.isIE) { - setTimeout(function () { - window.scrollBy(0, fullTop - emptyTop); - }, 0); - } - else { - window.scrollBy(0, fullTop - emptyTop); - } - }; - - var init = function () { - - setupEvents(panels.input, applyTimeout); - makePreviewHtml(); - - if (panels.preview) { - panels.preview.scrollTop = 0; - } - }; - - init(); - }; - - - // This simulates a modal dialog box and asks for the URL when you - // click the hyperlink or image buttons. - // - // text: The html for the input box. - // defaultInputText: The default value that appears in the input box. - // callback: The function which is executed when the prompt is dismissed, either via OK or Cancel. - // It receives a single argument; either the entered text (if OK was chosen) or null (if Cancel - // was chosen). - ui.prompt = function (title, text, defaultInputText, callback) { - - // These variables need to be declared at this level since they are used - // in multiple functions. - var dialog; // The dialog box. - var input; // The text box where you enter the hyperlink. - - - if (defaultInputText === undefined) { - defaultInputText = ""; - } - - // Used as a keydown event handler. Esc dismisses the prompt. - // Key code 27 is ESC. - var checkEscape = function (key) { - var code = (key.charCode || key.keyCode); - if (code === 27) { - close(true); - } - }; - - // Dismisses the hyperlink input box. - // isCancel is true if we don't care about the input text. - // isCancel is false if we are going to keep the text. - var close = function (isCancel) { - util.removeEvent(doc.body, "keydown", checkEscape); - var text = input.value; - - if (isCancel) { - text = null; - } - else { - // Fixes common pasting errors. - text = text.replace(/^http:\/\/(https?|ftp):\/\//, '$1://'); - if (!/^(?:https?|ftp):\/\//.test(text)) - text = 'http://' + text; - } - - $(dialog).modal('hide'); - - callback(text); - return false; - }; - - - - // Create the text input box form/window. - var createDialog = function () { - // - - // The main dialog box. - dialog = doc.createElement("div"); - dialog.className = "modal hide fade"; - dialog.style.display = "none"; - - // The header. - var header = doc.createElement("div"); - header.className = "modal-header"; - header.innerHTML = '×

    ' + title + '

    '; - dialog.appendChild(header); - - // The body. - var body = doc.createElement("div"); - body.className = "modal-body"; - dialog.appendChild(body); - - // The footer. - var footer = doc.createElement("div"); - footer.className = "modal-footer"; - dialog.appendChild(footer); - - // The dialog text. - var question = doc.createElement("p"); - question.innerHTML = text; - question.style.padding = "5px"; - body.appendChild(question); - - // The web form container for the text box and buttons. - var form = doc.createElement("form"), - style = form.style; - form.onsubmit = function () { return close(false); }; - style.padding = "0"; - style.margin = "0"; - body.appendChild(form); - - // The input text box - input = doc.createElement("input"); - input.type = "text"; - input.value = defaultInputText; - style = input.style; - style.display = "block"; - style.width = "80%"; - style.marginLeft = style.marginRight = "auto"; - form.appendChild(input); - - // The ok button - var okButton = doc.createElement("button"); - okButton.className = "btn btn-primary"; - okButton.type = "button"; - okButton.onclick = function () { return close(false); }; - okButton.innerHTML = "OK"; - - // The cancel button - var cancelButton = doc.createElement("button"); - cancelButton.className = "btn btn-primary"; - cancelButton.type = "button"; - cancelButton.onclick = function () { return close(true); }; - cancelButton.innerHTML = "Cancel"; - - footer.appendChild(okButton); - footer.appendChild(cancelButton); - - util.addEvent(doc.body, "keydown", checkEscape); - - doc.body.appendChild(dialog); - - }; - - // Why is this in a zero-length timeout? - // Is it working around a browser bug? - setTimeout(function () { - - createDialog(); - - var defTextLen = defaultInputText.length; - if (input.selectionStart !== undefined) { - input.selectionStart = 0; - input.selectionEnd = defTextLen; - } - else if (input.createTextRange) { - var range = input.createTextRange(); - range.collapse(false); - range.moveStart("character", -defTextLen); - range.moveEnd("character", defTextLen); - range.select(); - } - - $(dialog).on('shown', function () { - input.focus(); - }) - - $(dialog).on('hidden', function () { - dialog.parentNode.removeChild(dialog); - }) - - $(dialog).modal() - - }, 0); - }; - - function UIManager(postfix, panels, undoManager, previewManager, commandManager, helpOptions) { - - var inputBox = panels.input, - buttons = {}; // buttons.undo, buttons.link, etc. The actual DOM elements. - - makeSpritedButtonRow(); - - var keyEvent = "keydown"; - if (uaSniffed.isOpera) { - keyEvent = "keypress"; - } - - util.addEvent(inputBox, keyEvent, function (key) { - - // Check to see if we have a button key and, if so execute the callback. - if ((key.ctrlKey || key.metaKey) && !key.altKey && !key.shiftKey) { - - var keyCode = key.charCode || key.keyCode; - var keyCodeStr = String.fromCharCode(keyCode).toLowerCase(); - - switch (keyCodeStr) { - case "b": - doClick(buttons.bold); - break; - case "i": - doClick(buttons.italic); - break; - case "l": - doClick(buttons.link); - break; - case "q": - doClick(buttons.quote); - break; - case "k": - doClick(buttons.code); - break; - case "g": - doClick(buttons.image); - break; - case "o": - doClick(buttons.olist); - break; - case "u": - doClick(buttons.ulist); - break; - case "h": - doClick(buttons.heading); - break; - case "r": - doClick(buttons.hr); - break; - case "y": - doClick(buttons.redo); - break; - case "z": - if (key.shiftKey) { - doClick(buttons.redo); - } - else { - doClick(buttons.undo); - } - break; - default: - return; - } - - - if (key.preventDefault) { - key.preventDefault(); - } - - if (window.event) { - window.event.returnValue = false; - } - } - }); - - // Auto-indent on shift-enter - util.addEvent(inputBox, "keyup", function (key) { - if (key.shiftKey && !key.ctrlKey && !key.metaKey) { - var keyCode = key.charCode || key.keyCode; - // Character 13 is Enter - if (keyCode === 13) { - var fakeButton = {}; - fakeButton.textOp = bindCommand("doAutoindent"); - doClick(fakeButton); - } - } - }); - - // special handler because IE clears the context of the textbox on ESC - if (uaSniffed.isIE) { - util.addEvent(inputBox, "keydown", function (key) { - var code = key.keyCode; - if (code === 27) { - return false; - } - }); - } - - - // Perform the button's action. - function doClick(button) { - - inputBox.focus(); - - if (button.textOp) { - - if (undoManager) { - undoManager.setCommandMode(); - } - - var state = new TextareaState(panels); - - if (!state) { - return; - } - - var chunks = state.getChunks(); - - // Some commands launch a "modal" prompt dialog. Javascript - // can't really make a modal dialog box and the WMD code - // will continue to execute while the dialog is displayed. - // This prevents the dialog pattern I'm used to and means - // I can't do something like this: - // - // var link = CreateLinkDialog(); - // makeMarkdownLink(link); - // - // Instead of this straightforward method of handling a - // dialog I have to pass any code which would execute - // after the dialog is dismissed (e.g. link creation) - // in a function parameter. - // - // Yes this is awkward and I think it sucks, but there's - // no real workaround. Only the image and link code - // create dialogs and require the function pointers. - var fixupInputArea = function () { - - inputBox.focus(); - - if (chunks) { - state.setChunks(chunks); - } - - state.restore(); - previewManager.refresh(); - }; - - var noCleanup = button.textOp(chunks, fixupInputArea); - - if (!noCleanup) { - fixupInputArea(); - } - - } - - if (button.execute) { - button.execute(undoManager); - } - }; - - function setupButton(button, isEnabled) { - - if (isEnabled) { - button.disabled = false; - - if (!button.isHelp) { - button.onclick = function () { - if (this.onmouseout) { - this.onmouseout(); - } - doClick(this); - return false; - } - } - } - else { - button.disabled = true; - } - } - - function bindCommand(method) { - if (typeof method === "string") - method = commandManager[method]; - return function () { method.apply(commandManager, arguments); } - } - - function makeSpritedButtonRow() { - - var buttonBar = panels.buttonBar; - var buttonRow = document.createElement("div"); - buttonRow.id = "wmd-button-row" + postfix; - buttonRow.className = 'btn-toolbar'; - buttonRow = buttonBar.appendChild(buttonRow); - - var makeButton = function (id, title, icon, textOp, group) { - var button = document.createElement("button"); - button.className = "btn"; - var buttonImage = document.createElement("i"); - buttonImage.className = icon; - button.id = id + postfix; - button.appendChild(buttonImage); - button.title = title; - $(button).tooltip({ placement: 'bottom' }) - if (textOp) - button.textOp = textOp; - setupButton(button, true); - if (group) { - group.appendChild(button); - } else { - buttonRow.appendChild(button); - } - return button; - }; - var makeGroup = function (num) { - var group = document.createElement("div"); - group.className = "btn-group wmd-button-group" + num; - group.id = "wmd-button-group" + num + postfix; - buttonRow.appendChild(group); - return group - } - - group1 = makeGroup(1); - buttons.bold = makeButton("wmd-bold-button", "Bold - Ctrl+B", "icon-drop", bindCommand("doBold"), group1); - buttons.italic = makeButton("wmd-italic-button", "Italic - Ctrl+I", "icon-font", bindCommand("doItalic"), group1); - - group2 = makeGroup(2); - buttons.link = makeButton("wmd-link-button", "Link - Ctrl+L", "icon-link", bindCommand(function (chunk, postProcessing) { - return this.doLinkOrImage(chunk, postProcessing, false); - }), group2); - buttons.quote = makeButton("wmd-quote-button", "Blockquote - Ctrl+Q", "icon-quote", bindCommand("doBlockquote"), group2); - buttons.code = makeButton("wmd-code-button", "Code Sample - Ctrl+K", "icon-code", bindCommand("doCode"), group2); - buttons.image = makeButton("wmd-image-button", "Image - Ctrl+G", "icon-picture", bindCommand(function (chunk, postProcessing) { - return this.doLinkOrImage(chunk, postProcessing, true); - }), group2); - - group3 = makeGroup(3); - buttons.olist = makeButton("wmd-olist-button", "Numbered List - Ctrl+O", "icon-ordered-list", bindCommand(function (chunk, postProcessing) { - this.doList(chunk, postProcessing, true); - }), group3); - buttons.ulist = makeButton("wmd-ulist-button", "Bulleted List - Ctrl+U", "icon-bulleted-list", bindCommand(function (chunk, postProcessing) { - this.doList(chunk, postProcessing, false); - }), group3); - buttons.heading = makeButton("wmd-heading-button", "Heading - Ctrl+H", "icon-shift", bindCommand("doHeading"), group3); - buttons.hr = makeButton("wmd-hr-button", "Horizontal Rule - Ctrl+R", "icon-remove", bindCommand("doHorizontalRule"), group3); - - group4 = makeGroup(4); - buttons.undo = makeButton("wmd-undo-button", "Undo - Ctrl+Z", "icon-undo", null, group4); - buttons.undo.execute = function (manager) { if (manager) manager.undo(); }; - - var redoTitle = /win/.test(nav.platform.toLowerCase()) ? - "Redo - Ctrl+Y" : - "Redo - Ctrl+Shift+Z"; // mac and other non-Windows platforms - - buttons.redo = makeButton("wmd-redo-button", redoTitle, "icon-share-alt", null, group4); - buttons.redo.execute = function (manager) { if (manager) manager.redo(); }; - - if (helpOptions) { - group5 = makeGroup(5); - group5.className = group5.className + " pull-right"; - var helpButton = document.createElement("button"); - var helpButtonImage = document.createElement("i"); - helpButtonImage.className = "icon-question-sign"; - helpButton.appendChild(helpButtonImage); - helpButton.className = "btn"; - helpButton.id = "wmd-help-button" + postfix; - helpButton.isHelp = true; - helpButton.title = helpOptions.title || defaultHelpHoverTitle; - $(helpButton).tooltip({ placement: 'bottom' }) - helpButton.onclick = helpOptions.handler; - - setupButton(helpButton, true); - group5.appendChild(helpButton); - buttons.help = helpButton; - } - - setUndoRedoButtonStates(); - } - - function setUndoRedoButtonStates() { - if (undoManager) { - setupButton(buttons.undo, undoManager.canUndo()); - setupButton(buttons.redo, undoManager.canRedo()); - } - }; - - this.setUndoRedoButtonStates = setUndoRedoButtonStates; - - } - - function CommandManager(pluginHooks) { - this.hooks = pluginHooks; - } - - var commandProto = CommandManager.prototype; - - // The markdown symbols - 4 spaces = code, > = blockquote, etc. - commandProto.prefixes = "(?:\\s{4,}|\\s*>|\\s*-\\s+|\\s*\\d+\\.|=|\\+|-|_|\\*|#|\\s*\\[[^\n]]+\\]:)"; - - // Remove markdown symbols from the chunk selection. - commandProto.unwrap = function (chunk) { - var txt = new re("([^\\n])\\n(?!(\\n|" + this.prefixes + "))", "g"); - chunk.selection = chunk.selection.replace(txt, "$1 $2"); - }; - - commandProto.wrap = function (chunk, len) { - this.unwrap(chunk); - var regex = new re("(.{1," + len + "})( +|$\\n?)", "gm"), - that = this; - - chunk.selection = chunk.selection.replace(regex, function (line, marked) { - if (new re("^" + that.prefixes, "").test(line)) { - return line; - } - return marked + "\n"; - }); - - chunk.selection = chunk.selection.replace(/\s+$/, ""); - }; - - commandProto.doBold = function (chunk, postProcessing) { - return this.doBorI(chunk, postProcessing, 2, "strong text"); - }; - - commandProto.doItalic = function (chunk, postProcessing) { - return this.doBorI(chunk, postProcessing, 1, "emphasized text"); - }; - - // chunk: The selected region that will be enclosed with */** - // nStars: 1 for italics, 2 for bold - // insertText: If you just click the button without highlighting text, this gets inserted - commandProto.doBorI = function (chunk, postProcessing, nStars, insertText) { - - // Get rid of whitespace and fixup newlines. - chunk.trimWhitespace(); - chunk.selection = chunk.selection.replace(/\n{2,}/g, "\n"); - - // Look for stars before and after. Is the chunk already marked up? - // note that these regex matches cannot fail - var starsBefore = /(\**$)/.exec(chunk.before)[0]; - var starsAfter = /(^\**)/.exec(chunk.after)[0]; - - var prevStars = Math.min(starsBefore.length, starsAfter.length); - - // Remove stars if we have to since the button acts as a toggle. - if ((prevStars >= nStars) && (prevStars != 2 || nStars != 1)) { - chunk.before = chunk.before.replace(re("[*]{" + nStars + "}$", ""), ""); - chunk.after = chunk.after.replace(re("^[*]{" + nStars + "}", ""), ""); - } - else if (!chunk.selection && starsAfter) { - // It's not really clear why this code is necessary. It just moves - // some arbitrary stuff around. - chunk.after = chunk.after.replace(/^([*_]*)/, ""); - chunk.before = chunk.before.replace(/(\s?)$/, ""); - var whitespace = re.$1; - chunk.before = chunk.before + starsAfter + whitespace; - } - else { - - // In most cases, if you don't have any selected text and click the button - // you'll get a selected, marked up region with the default text inserted. - if (!chunk.selection && !starsAfter) { - chunk.selection = insertText; - } - - // Add the true markup. - var markup = nStars <= 1 ? "*" : "**"; // shouldn't the test be = ? - chunk.before = chunk.before + markup; - chunk.after = markup + chunk.after; - } - - return; - }; - - commandProto.stripLinkDefs = function (text, defsToAdd) { - - text = text.replace(/^[ ]{0,3}\[(\d+)\]:[ \t]*\n?[ \t]*?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|$)/gm, - function (totalMatch, id, link, newlines, title) { - defsToAdd[id] = totalMatch.replace(/\s*$/, ""); - if (newlines) { - // Strip the title and return that separately. - defsToAdd[id] = totalMatch.replace(/["(](.+?)[")]$/, ""); - return newlines + title; - } - return ""; - }); - - return text; - }; - - commandProto.addLinkDef = function (chunk, linkDef) { - - var refNumber = 0; // The current reference number - var defsToAdd = {}; // - // Start with a clean slate by removing all previous link definitions. - chunk.before = this.stripLinkDefs(chunk.before, defsToAdd); - chunk.selection = this.stripLinkDefs(chunk.selection, defsToAdd); - chunk.after = this.stripLinkDefs(chunk.after, defsToAdd); - - var defs = ""; - var regex = /(\[)((?:\[[^\]]*\]|[^\[\]])*)(\][ ]?(?:\n[ ]*)?\[)(\d+)(\])/g; - - var addDefNumber = function (def) { - refNumber++; - def = def.replace(/^[ ]{0,3}\[(\d+)\]:/, " [" + refNumber + "]:"); - defs += "\n" + def; - }; - - // note that - // a) the recursive call to getLink cannot go infinite, because by definition - // of regex, inner is always a proper substring of wholeMatch, and - // b) more than one level of nesting is neither supported by the regex - // nor making a lot of sense (the only use case for nesting is a linked image) - var getLink = function (wholeMatch, before, inner, afterInner, id, end) { - inner = inner.replace(regex, getLink); - if (defsToAdd[id]) { - addDefNumber(defsToAdd[id]); - return before + inner + afterInner + refNumber + end; - } - return wholeMatch; - }; - - chunk.before = chunk.before.replace(regex, getLink); - - if (linkDef) { - addDefNumber(linkDef); - } - else { - chunk.selection = chunk.selection.replace(regex, getLink); - } - - var refOut = refNumber; - - chunk.after = chunk.after.replace(regex, getLink); - - if (chunk.after) { - chunk.after = chunk.after.replace(/\n*$/, ""); - } - if (!chunk.after) { - chunk.selection = chunk.selection.replace(/\n*$/, ""); - } - - chunk.after += "\n\n" + defs; - - return refOut; - }; - - // takes the line as entered into the add link/as image dialog and makes - // sure the URL and the optinal title are "nice". - function properlyEncoded(linkdef) { - return linkdef.replace(/^\s*(.*?)(?:\s+"(.+)")?\s*$/, function (wholematch, link, title) { - link = link.replace(/\?.*$/, function (querypart) { - return querypart.replace(/\+/g, " "); // in the query string, a plus and a space are identical - }); - link = decodeURIComponent(link); // unencode first, to prevent double encoding - link = encodeURI(link).replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29'); - link = link.replace(/\?.*$/, function (querypart) { - return querypart.replace(/\+/g, "%2b"); // since we replaced plus with spaces in the query part, all pluses that now appear where originally encoded - }); - if (title) { - title = title.trim ? title.trim() : title.replace(/^\s*/, "").replace(/\s*$/, ""); - title = $.trim(title).replace(/"/g, "quot;").replace(/\(/g, "(").replace(/\)/g, ")").replace(//g, ">"); - } - return title ? link + ' "' + title + '"' : link; - }); - } - - commandProto.doLinkOrImage = function (chunk, postProcessing, isImage) { - - chunk.trimWhitespace(); - chunk.findTags(/\s*!?\[/, /\][ ]?(?:\n[ ]*)?(\[.*?\])?/); - var background; - - if (chunk.endTag.length > 1 && chunk.startTag.length > 0) { - - chunk.startTag = chunk.startTag.replace(/!?\[/, ""); - chunk.endTag = ""; - this.addLinkDef(chunk, null); - - } - else { - - // We're moving start and end tag back into the selection, since (as we're in the else block) we're not - // *removing* a link, but *adding* one, so whatever findTags() found is now back to being part of the - // link text. linkEnteredCallback takes care of escaping any brackets. - chunk.selection = chunk.startTag + chunk.selection + chunk.endTag; - chunk.startTag = chunk.endTag = ""; - - if (/\n\n/.test(chunk.selection)) { - this.addLinkDef(chunk, null); - return; - } - var that = this; - // The function to be executed when you enter a link and press OK or Cancel. - // Marks up the link and adds the ref. - var linkEnteredCallback = function (link) { - - if (link !== null) { - // ( $1 - // [^\\] anything that's not a backslash - // (?:\\\\)* an even number (this includes zero) of backslashes - // ) - // (?= followed by - // [[\]] an opening or closing bracket - // ) - // - // In other words, a non-escaped bracket. These have to be escaped now to make sure they - // don't count as the end of the link or similar. - // Note that the actual bracket has to be a lookahead, because (in case of to subsequent brackets), - // the bracket in one match may be the "not a backslash" character in the next match, so it - // should not be consumed by the first match. - // The "prepend a space and finally remove it" steps makes sure there is a "not a backslash" at the - // start of the string, so this also works if the selection begins with a bracket. We cannot solve - // this by anchoring with ^, because in the case that the selection starts with two brackets, this - // would mean a zero-width match at the start. Since zero-width matches advance the string position, - // the first bracket could then not act as the "not a backslash" for the second. - chunk.selection = (" " + chunk.selection).replace(/([^\\](?:\\\\)*)(?=[[\]])/g, "$1\\").substr(1); - - var linkDef = " [999]: " + properlyEncoded(link); - - var num = that.addLinkDef(chunk, linkDef); - chunk.startTag = isImage ? "![" : "["; - chunk.endTag = "][" + num + "]"; - - if (!chunk.selection) { - if (isImage) { - chunk.selection = "enter image description here"; - } - else { - chunk.selection = "enter link description here"; - } - } - } - postProcessing(); - }; - - - if (isImage) { - if (!this.hooks.insertImageDialog(linkEnteredCallback)) - ui.prompt('Insert Image', imageDialogText, imageDefaultText, linkEnteredCallback); - } - else { - ui.prompt('Insert Link', linkDialogText, linkDefaultText, linkEnteredCallback); - } - return true; - } - }; - - // When making a list, hitting shift-enter will put your cursor on the next line - // at the current indent level. - commandProto.doAutoindent = function (chunk, postProcessing) { - - var commandMgr = this, - fakeSelection = false; - - chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]*\n$/, "\n\n"); - chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}>[ \t]*\n$/, "\n\n"); - chunk.before = chunk.before.replace(/(\n|^)[ \t]+\n$/, "\n\n"); - - // There's no selection, end the cursor wasn't at the end of the line: - // The user wants to split the current list item / code line / blockquote line - // (for the latter it doesn't really matter) in two. Temporarily select the - // (rest of the) line to achieve this. - if (!chunk.selection && !/^[ \t]*(?:\n|$)/.test(chunk.after)) { - chunk.after = chunk.after.replace(/^[^\n]*/, function (wholeMatch) { - chunk.selection = wholeMatch; - return ""; - }); - fakeSelection = true; - } - - if (/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]+.*\n$/.test(chunk.before)) { - if (commandMgr.doList) { - commandMgr.doList(chunk); - } - } - if (/(\n|^)[ ]{0,3}>[ \t]+.*\n$/.test(chunk.before)) { - if (commandMgr.doBlockquote) { - commandMgr.doBlockquote(chunk); - } - } - if (/(\n|^)(\t|[ ]{4,}).*\n$/.test(chunk.before)) { - if (commandMgr.doCode) { - commandMgr.doCode(chunk); - } - } - - if (fakeSelection) { - chunk.after = chunk.selection + chunk.after; - chunk.selection = ""; - } - }; - - commandProto.doBlockquote = function (chunk, postProcessing) { - - chunk.selection = chunk.selection.replace(/^(\n*)([^\r]+?)(\n*)$/, - function (totalMatch, newlinesBefore, text, newlinesAfter) { - chunk.before += newlinesBefore; - chunk.after = newlinesAfter + chunk.after; - return text; - }); - - chunk.before = chunk.before.replace(/(>[ \t]*)$/, - function (totalMatch, blankLine) { - chunk.selection = blankLine + chunk.selection; - return ""; - }); - - chunk.selection = chunk.selection.replace(/^(\s|>)+$/, ""); - chunk.selection = chunk.selection || "Blockquote"; - - // The original code uses a regular expression to find out how much of the - // text *directly before* the selection already was a blockquote: - - /* - if (chunk.before) { - chunk.before = chunk.before.replace(/\n?$/, "\n"); - } - chunk.before = chunk.before.replace(/(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*$)/, - function (totalMatch) { - chunk.startTag = totalMatch; - return ""; - }); - */ - - // This comes down to: - // Go backwards as many lines a possible, such that each line - // a) starts with ">", or - // b) is almost empty, except for whitespace, or - // c) is preceeded by an unbroken chain of non-empty lines - // leading up to a line that starts with ">" and at least one more character - // and in addition - // d) at least one line fulfills a) - // - // Since this is essentially a backwards-moving regex, it's susceptible to - // catstrophic backtracking and can cause the browser to hang; - // see e.g. http://meta.stackoverflow.com/questions/9807. - // - // Hence we replaced this by a simple state machine that just goes through the - // lines and checks for a), b), and c). - - var match = "", - leftOver = "", - line; - if (chunk.before) { - var lines = chunk.before.replace(/\n$/, "").split("\n"); - var inChain = false; - for (var i = 0; i < lines.length; i++) { - var good = false; - line = lines[i]; - inChain = inChain && line.length > 0; // c) any non-empty line continues the chain - if (/^>/.test(line)) { // a) - good = true; - if (!inChain && line.length > 1) // c) any line that starts with ">" and has at least one more character starts the chain - inChain = true; - } else if (/^[ \t]*$/.test(line)) { // b) - good = true; - } else { - good = inChain; // c) the line is not empty and does not start with ">", so it matches if and only if we're in the chain - } - if (good) { - match += line + "\n"; - } else { - leftOver += match + line; - match = "\n"; - } - } - if (!/(^|\n)>/.test(match)) { // d) - leftOver += match; - match = ""; - } - } - - chunk.startTag = match; - chunk.before = leftOver; - - // end of change - - if (chunk.after) { - chunk.after = chunk.after.replace(/^\n?/, "\n"); - } - - chunk.after = chunk.after.replace(/^(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*)/, - function (totalMatch) { - chunk.endTag = totalMatch; - return ""; - } - ); - - var replaceBlanksInTags = function (useBracket) { - - var replacement = useBracket ? "> " : ""; - - if (chunk.startTag) { - chunk.startTag = chunk.startTag.replace(/\n((>|\s)*)\n$/, - function (totalMatch, markdown) { - return "\n" + markdown.replace(/^[ ]{0,3}>?[ \t]*$/gm, replacement) + "\n"; - }); - } - if (chunk.endTag) { - chunk.endTag = chunk.endTag.replace(/^\n((>|\s)*)\n/, - function (totalMatch, markdown) { - return "\n" + markdown.replace(/^[ ]{0,3}>?[ \t]*$/gm, replacement) + "\n"; - }); - } - }; - - if (/^(?![ ]{0,3}>)/m.test(chunk.selection)) { - this.wrap(chunk, SETTINGS.lineLength - 2); - chunk.selection = chunk.selection.replace(/^/gm, "> "); - replaceBlanksInTags(true); - chunk.skipLines(); - } else { - chunk.selection = chunk.selection.replace(/^[ ]{0,3}> ?/gm, ""); - this.unwrap(chunk); - replaceBlanksInTags(false); - - if (!/^(\n|^)[ ]{0,3}>/.test(chunk.selection) && chunk.startTag) { - chunk.startTag = chunk.startTag.replace(/\n{0,2}$/, "\n\n"); - } - - if (!/(\n|^)[ ]{0,3}>.*$/.test(chunk.selection) && chunk.endTag) { - chunk.endTag = chunk.endTag.replace(/^\n{0,2}/, "\n\n"); - } - } - - chunk.selection = this.hooks.postBlockquoteCreation(chunk.selection); - - if (!/\n/.test(chunk.selection)) { - chunk.selection = chunk.selection.replace(/^(> *)/, - function (wholeMatch, blanks) { - chunk.startTag += blanks; - return ""; - }); - } - }; - - commandProto.doCode = function (chunk, postProcessing) { - - var hasTextBefore = /\S[ ]*$/.test(chunk.before); - var hasTextAfter = /^[ ]*\S/.test(chunk.after); - - // Use 'four space' markdown if the selection is on its own - // line or is multiline. - if ((!hasTextAfter && !hasTextBefore) || /\n/.test(chunk.selection)) { - - chunk.before = chunk.before.replace(/[ ]{4}$/, - function (totalMatch) { - chunk.selection = totalMatch + chunk.selection; - return ""; - }); - - var nLinesBack = 1; - var nLinesForward = 1; - - if (/(\n|^)(\t|[ ]{4,}).*\n$/.test(chunk.before)) { - nLinesBack = 0; - } - if (/^\n(\t|[ ]{4,})/.test(chunk.after)) { - nLinesForward = 0; - } - - chunk.skipLines(nLinesBack, nLinesForward); - - if (!chunk.selection) { - chunk.startTag = " "; - chunk.selection = "enter code here"; - } - else { - if (/^[ ]{0,3}\S/m.test(chunk.selection)) { - if (/\n/.test(chunk.selection)) - chunk.selection = chunk.selection.replace(/^/gm, " "); - else // if it's not multiline, do not select the four added spaces; this is more consistent with the doList behavior - chunk.before += " "; - } - else { - chunk.selection = chunk.selection.replace(/^[ ]{4}/gm, ""); - } - } - } - else { - // Use backticks (`) to delimit the code block. - - chunk.trimWhitespace(); - chunk.findTags(/`/, /`/); - - if (!chunk.startTag && !chunk.endTag) { - chunk.startTag = chunk.endTag = "`"; - if (!chunk.selection) { - chunk.selection = "enter code here"; - } - } - else if (chunk.endTag && !chunk.startTag) { - chunk.before += chunk.endTag; - chunk.endTag = ""; - } - else { - chunk.startTag = chunk.endTag = ""; - } - } - }; - - commandProto.doList = function (chunk, postProcessing, isNumberedList) { - - // These are identical except at the very beginning and end. - // Should probably use the regex extension function to make this clearer. - var previousItemsRegex = /(\n|^)(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*$/; - var nextItemsRegex = /^\n*(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*/; - - // The default bullet is a dash but others are possible. - // This has nothing to do with the particular HTML bullet, - // it's just a markdown bullet. - var bullet = "-"; - - // The number in a numbered list. - var num = 1; - - // Get the item prefix - e.g. " 1. " for a numbered list, " - " for a bulleted list. - var getItemPrefix = function () { - var prefix; - if (isNumberedList) { - prefix = " " + num + ". "; - num++; - } - else { - prefix = " " + bullet + " "; - } - return prefix; - }; - - // Fixes the prefixes of the other list items. - var getPrefixedItem = function (itemText) { - - // The numbering flag is unset when called by autoindent. - if (isNumberedList === undefined) { - isNumberedList = /^\s*\d/.test(itemText); - } - - // Renumber/bullet the list element. - itemText = itemText.replace(/^[ ]{0,3}([*+-]|\d+[.])\s/gm, - function (_) { - return getItemPrefix(); - }); - - return itemText; - }; - - chunk.findTags(/(\n|^)*[ ]{0,3}([*+-]|\d+[.])\s+/, null); - - if (chunk.before && !/\n$/.test(chunk.before) && !/^\n/.test(chunk.startTag)) { - chunk.before += chunk.startTag; - chunk.startTag = ""; - } - - if (chunk.startTag) { - - var hasDigits = /\d+[.]/.test(chunk.startTag); - chunk.startTag = ""; - chunk.selection = chunk.selection.replace(/\n[ ]{4}/g, "\n"); - this.unwrap(chunk); - chunk.skipLines(); - - if (hasDigits) { - // Have to renumber the bullet points if this is a numbered list. - chunk.after = chunk.after.replace(nextItemsRegex, getPrefixedItem); - } - if (isNumberedList == hasDigits) { - return; - } - } - - var nLinesUp = 1; - - chunk.before = chunk.before.replace(previousItemsRegex, - function (itemText) { - if (/^\s*([*+-])/.test(itemText)) { - bullet = re.$1; - } - nLinesUp = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0; - return getPrefixedItem(itemText); - }); - - if (!chunk.selection) { - chunk.selection = "List item"; - } - - var prefix = getItemPrefix(); - - var nLinesDown = 1; - - chunk.after = chunk.after.replace(nextItemsRegex, - function (itemText) { - nLinesDown = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0; - return getPrefixedItem(itemText); - }); - - chunk.trimWhitespace(true); - chunk.skipLines(nLinesUp, nLinesDown, true); - chunk.startTag = prefix; - var spaces = prefix.replace(/./g, " "); - this.wrap(chunk, SETTINGS.lineLength - spaces.length); - chunk.selection = chunk.selection.replace(/\n/g, "\n" + spaces); - - }; - - commandProto.doHeading = function (chunk, postProcessing) { - - // Remove leading/trailing whitespace and reduce internal spaces to single spaces. - chunk.selection = chunk.selection.replace(/\s+/g, " "); - chunk.selection = chunk.selection.replace(/(^\s+|\s+$)/g, ""); - - // If we clicked the button with no selected text, we just - // make a level 2 hash header around some default text. - if (!chunk.selection) { - chunk.startTag = "## "; - chunk.selection = "Heading"; - chunk.endTag = " ##"; - return; - } - - var headerLevel = 0; // The existing header level of the selected text. - - // Remove any existing hash heading markdown and save the header level. - chunk.findTags(/#+[ ]*/, /[ ]*#+/); - if (/#+/.test(chunk.startTag)) { - headerLevel = re.lastMatch.length; - } - chunk.startTag = chunk.endTag = ""; - - // Try to get the current header level by looking for - and = in the line - // below the selection. - chunk.findTags(null, /\s?(-+|=+)/); - if (/=+/.test(chunk.endTag)) { - headerLevel = 1; - } - if (/-+/.test(chunk.endTag)) { - headerLevel = 2; - } - - // Skip to the next line so we can create the header markdown. - chunk.startTag = chunk.endTag = ""; - chunk.skipLines(1, 1); - - // We make a level 2 header if there is no current header. - // If there is a header level, we substract one from the header level. - // If it's already a level 1 header, it's removed. - var headerLevelToCreate = headerLevel == 0 ? 2 : headerLevel - 1; - - if (headerLevelToCreate > 0) { - - // The button only creates level 1 and 2 underline headers. - // Why not have it iterate over hash header levels? Wouldn't that be easier and cleaner? - var headerChar = headerLevelToCreate >= 2 ? "-" : "="; - var len = chunk.selection.length; - if (len > SETTINGS.lineLength) { - len = SETTINGS.lineLength; - } - chunk.endTag = "\n"; - while (len--) { - chunk.endTag += headerChar; - } - } - }; - - commandProto.doHorizontalRule = function (chunk, postProcessing) { - chunk.startTag = "----------\n"; - chunk.selection = ""; - chunk.skipLines(2, 1, true); - } - - +// needs Markdown.Converter.js at the moment + +(function () { + + var util = {}, + position = {}, + ui = {}, + doc = window.document, + re = window.RegExp, + nav = window.navigator, + SETTINGS = { lineLength: 72 }, + + // Used to work around some browser bugs where we can't use feature testing. + uaSniffed = { + isIE: /msie/.test(nav.userAgent.toLowerCase()), + isIE_5or6: /msie 6/.test(nav.userAgent.toLowerCase()) || /msie 5/.test(nav.userAgent.toLowerCase()), + isOpera: /opera/.test(nav.userAgent.toLowerCase()) + }; + + + // ------------------------------------------------------------------- + // YOUR CHANGES GO HERE + // + // I've tried to localize the things you are likely to change to + // this area. + // ------------------------------------------------------------------- + + // The text that appears on the upper part of the dialog box when + // entering links. + var linkDialogText = "

    http://example.com/ \"optional title\"

    "; + var imageDialogText = "

    http://example.com/images/diagram.jpg \"optional title\"

    "; + + // The default text that appears in the dialog input box when entering + // links. + var imageDefaultText = "http://"; + var linkDefaultText = "http://"; + + var defaultHelpHoverTitle = "Markdown Editing Help"; + + // ------------------------------------------------------------------- + // END OF YOUR CHANGES + // ------------------------------------------------------------------- + + // help, if given, should have a property "handler", the click handler for the help button, + // and can have an optional property "title" for the button's tooltip (defaults to "Markdown Editing Help"). + // If help isn't given, not help button is created. + // + // The constructed editor object has the methods: + // - getConverter() returns the markdown converter object that was passed to the constructor + // - run() actually starts the editor; should be called after all necessary plugins are registered. Calling this more than once is a no-op. + // - refreshPreview() forces the preview to be updated. This method is only available after run() was called. + Markdown.Editor = function (markdownConverter, idPostfix, help) { + + idPostfix = idPostfix || ""; + + var hooks = this.hooks = new Markdown.HookCollection(); + hooks.addNoop("onPreviewRefresh"); // called with no arguments after the preview has been refreshed + hooks.addNoop("postBlockquoteCreation"); // called with the user's selection *after* the blockquote was created; should return the actual to-be-inserted text + hooks.addFalse("insertImageDialog"); /* called with one parameter: a callback to be called with the URL of the image. If the application creates + * its own image insertion dialog, this hook should return true, and the callback should be called with the chosen + * image url (or null if the user cancelled). If this hook returns false, the default dialog will be used. + */ + + this.getConverter = function () { return markdownConverter; } + + var that = this, + panels; + + this.run = function () { + if (panels) + return; // already initialized + + panels = new PanelCollection(idPostfix); + var commandManager = new CommandManager(hooks); + var previewManager = new PreviewManager(markdownConverter, panels, function () { hooks.onPreviewRefresh(); }); + var undoManager, uiManager; + + if (!/\?noundo/.test(doc.location.href)) { + undoManager = new UndoManager(function () { + previewManager.refresh(); + if (uiManager) // not available on the first call + uiManager.setUndoRedoButtonStates(); + }, panels); + this.textOperation = function (f) { + undoManager.setCommandMode(); + f(); + that.refreshPreview(); + } + } + + uiManager = new UIManager(idPostfix, panels, undoManager, previewManager, commandManager, help); + uiManager.setUndoRedoButtonStates(); + + var forceRefresh = that.refreshPreview = function () { previewManager.refresh(true); }; + + forceRefresh(); + }; + + } + + // before: contains all the text in the input box BEFORE the selection. + // after: contains all the text in the input box AFTER the selection. + function Chunks() { } + + // startRegex: a regular expression to find the start tag + // endRegex: a regular expresssion to find the end tag + Chunks.prototype.findTags = function (startRegex, endRegex) { + + var chunkObj = this; + var regex; + + if (startRegex) { + + regex = util.extendRegExp(startRegex, "", "$"); + + this.before = this.before.replace(regex, + function (match) { + chunkObj.startTag = chunkObj.startTag + match; + return ""; + }); + + regex = util.extendRegExp(startRegex, "^", ""); + + this.selection = this.selection.replace(regex, + function (match) { + chunkObj.startTag = chunkObj.startTag + match; + return ""; + }); + } + + if (endRegex) { + + regex = util.extendRegExp(endRegex, "", "$"); + + this.selection = this.selection.replace(regex, + function (match) { + chunkObj.endTag = match + chunkObj.endTag; + return ""; + }); + + regex = util.extendRegExp(endRegex, "^", ""); + + this.after = this.after.replace(regex, + function (match) { + chunkObj.endTag = match + chunkObj.endTag; + return ""; + }); + } + }; + + // If remove is false, the whitespace is transferred + // to the before/after regions. + // + // If remove is true, the whitespace disappears. + Chunks.prototype.trimWhitespace = function (remove) { + var beforeReplacer, afterReplacer, that = this; + if (remove) { + beforeReplacer = afterReplacer = ""; + } else { + beforeReplacer = function (s) { that.before += s; return ""; } + afterReplacer = function (s) { that.after = s + that.after; return ""; } + } + + this.selection = this.selection.replace(/^(\s*)/, beforeReplacer).replace(/(\s*)$/, afterReplacer); + }; + + + Chunks.prototype.skipLines = function (nLinesBefore, nLinesAfter, findExtraNewlines) { + + if (nLinesBefore === undefined) { + nLinesBefore = 1; + } + + if (nLinesAfter === undefined) { + nLinesAfter = 1; + } + + nLinesBefore++; + nLinesAfter++; + + var regexText; + var replacementText; + + // chrome bug ... documented at: http://meta.stackoverflow.com/questions/63307/blockquote-glitch-in-editor-in-chrome-6-and-7/65985#65985 + if (navigator.userAgent.match(/Chrome/)) { + "X".match(/()./); + } + + this.selection = this.selection.replace(/(^\n*)/, ""); + + this.startTag = this.startTag + re.$1; + + this.selection = this.selection.replace(/(\n*$)/, ""); + this.endTag = this.endTag + re.$1; + this.startTag = this.startTag.replace(/(^\n*)/, ""); + this.before = this.before + re.$1; + this.endTag = this.endTag.replace(/(\n*$)/, ""); + this.after = this.after + re.$1; + + if (this.before) { + + regexText = replacementText = ""; + + while (nLinesBefore--) { + regexText += "\\n?"; + replacementText += "\n"; + } + + if (findExtraNewlines) { + regexText = "\\n*"; + } + this.before = this.before.replace(new re(regexText + "$", ""), replacementText); + } + + if (this.after) { + + regexText = replacementText = ""; + + while (nLinesAfter--) { + regexText += "\\n?"; + replacementText += "\n"; + } + if (findExtraNewlines) { + regexText = "\\n*"; + } + + this.after = this.after.replace(new re(regexText, ""), replacementText); + } + }; + + // end of Chunks + + // A collection of the important regions on the page. + // Cached so we don't have to keep traversing the DOM. + // Also holds ieCachedRange and ieCachedScrollTop, where necessary; working around + // this issue: + // Internet explorer has problems with CSS sprite buttons that use HTML + // lists. When you click on the background image "button", IE will + // select the non-existent link text and discard the selection in the + // textarea. The solution to this is to cache the textarea selection + // on the button's mousedown event and set a flag. In the part of the + // code where we need to grab the selection, we check for the flag + // and, if it's set, use the cached area instead of querying the + // textarea. + // + // This ONLY affects Internet Explorer (tested on versions 6, 7 + // and 8) and ONLY on button clicks. Keyboard shortcuts work + // normally since the focus never leaves the textarea. + function PanelCollection(postfix) { + this.buttonBar = doc.getElementById("wmd-button-bar" + postfix); + this.preview = doc.getElementById("wmd-preview" + postfix); + this.input = doc.getElementById("wmd-input" + postfix); + }; + + // Returns true if the DOM element is visible, false if it's hidden. + // Checks if display is anything other than none. + util.isVisible = function (elem) { + + if (window.getComputedStyle) { + // Most browsers + return window.getComputedStyle(elem, null).getPropertyValue("display") !== "none"; + } + else if (elem.currentStyle) { + // IE + return elem.currentStyle["display"] !== "none"; + } + }; + + + // Adds a listener callback to a DOM element which is fired on a specified + // event. + util.addEvent = function (elem, event, listener) { + if (elem.attachEvent) { + // IE only. The "on" is mandatory. + elem.attachEvent("on" + event, listener); + } + else { + // Other browsers. + elem.addEventListener(event, listener, false); + } + }; + + + // Removes a listener callback from a DOM element which is fired on a specified + // event. + util.removeEvent = function (elem, event, listener) { + if (elem.detachEvent) { + // IE only. The "on" is mandatory. + elem.detachEvent("on" + event, listener); + } + else { + // Other browsers. + elem.removeEventListener(event, listener, false); + } + }; + + // Converts \r\n and \r to \n. + util.fixEolChars = function (text) { + text = text.replace(/\r\n/g, "\n"); + text = text.replace(/\r/g, "\n"); + return text; + }; + + // Extends a regular expression. Returns a new RegExp + // using pre + regex + post as the expression. + // Used in a few functions where we have a base + // expression and we want to pre- or append some + // conditions to it (e.g. adding "$" to the end). + // The flags are unchanged. + // + // regex is a RegExp, pre and post are strings. + util.extendRegExp = function (regex, pre, post) { + + if (pre === null || pre === undefined) { + pre = ""; + } + if (post === null || post === undefined) { + post = ""; + } + + var pattern = regex.toString(); + var flags; + + // Replace the flags with empty space and store them. + pattern = pattern.replace(/\/([gim]*)$/, function (wholeMatch, flagsPart) { + flags = flagsPart; + return ""; + }); + + // Remove the slash delimiters on the regular expression. + pattern = pattern.replace(/(^\/|\/$)/g, ""); + pattern = pre + pattern + post; + + return new re(pattern, flags); + } + + // UNFINISHED + // The assignment in the while loop makes jslint cranky. + // I'll change it to a better loop later. + position.getTop = function (elem, isInner) { + var result = elem.offsetTop; + if (!isInner) { + while (elem = elem.offsetParent) { + result += elem.offsetTop; + } + } + return result; + }; + + position.getHeight = function (elem) { + return elem.offsetHeight || elem.scrollHeight; + }; + + position.getWidth = function (elem) { + return elem.offsetWidth || elem.scrollWidth; + }; + + position.getPageSize = function () { + + var scrollWidth, scrollHeight; + var innerWidth, innerHeight; + + // It's not very clear which blocks work with which browsers. + if (self.innerHeight && self.scrollMaxY) { + scrollWidth = doc.body.scrollWidth; + scrollHeight = self.innerHeight + self.scrollMaxY; + } + else if (doc.body.scrollHeight > doc.body.offsetHeight) { + scrollWidth = doc.body.scrollWidth; + scrollHeight = doc.body.scrollHeight; + } + else { + scrollWidth = doc.body.offsetWidth; + scrollHeight = doc.body.offsetHeight; + } + + if (self.innerHeight) { + // Non-IE browser + innerWidth = self.innerWidth; + innerHeight = self.innerHeight; + } + else if (doc.documentElement && doc.documentElement.clientHeight) { + // Some versions of IE (IE 6 w/ a DOCTYPE declaration) + innerWidth = doc.documentElement.clientWidth; + innerHeight = doc.documentElement.clientHeight; + } + else if (doc.body) { + // Other versions of IE + innerWidth = doc.body.clientWidth; + innerHeight = doc.body.clientHeight; + } + + var maxWidth = Math.max(scrollWidth, innerWidth); + var maxHeight = Math.max(scrollHeight, innerHeight); + return [maxWidth, maxHeight, innerWidth, innerHeight]; + }; + + // Handles pushing and popping TextareaStates for undo/redo commands. + // I should rename the stack variables to list. + function UndoManager(callback, panels) { + + var undoObj = this; + var undoStack = []; // A stack of undo states + var stackPtr = 0; // The index of the current state + var mode = "none"; + var lastState; // The last state + var timer; // The setTimeout handle for cancelling the timer + var inputStateObj; + + // Set the mode for later logic steps. + var setMode = function (newMode, noSave) { + if (mode != newMode) { + mode = newMode; + if (!noSave) { + saveState(); + } + } + + if (!uaSniffed.isIE || mode != "moving") { + timer = setTimeout(refreshState, 1); + } + else { + inputStateObj = null; + } + }; + + var refreshState = function (isInitialState) { + inputStateObj = new TextareaState(panels, isInitialState); + timer = undefined; + }; + + this.setCommandMode = function () { + mode = "command"; + saveState(); + timer = setTimeout(refreshState, 0); + }; + + this.canUndo = function () { + return stackPtr > 1; + }; + + this.canRedo = function () { + if (undoStack[stackPtr + 1]) { + return true; + } + return false; + }; + + // Removes the last state and restores it. + this.undo = function () { + + if (undoObj.canUndo()) { + if (lastState) { + // What about setting state -1 to null or checking for undefined? + lastState.restore(); + lastState = null; + } + else { + undoStack[stackPtr] = new TextareaState(panels); + undoStack[--stackPtr].restore(); + + if (callback) { + callback(); + } + } + } + + mode = "none"; + panels.input.focus(); + refreshState(); + }; + + // Redo an action. + this.redo = function () { + + if (undoObj.canRedo()) { + + undoStack[++stackPtr].restore(); + + if (callback) { + callback(); + } + } + + mode = "none"; + panels.input.focus(); + refreshState(); + }; + + // Push the input area state to the stack. + var saveState = function () { + var currState = inputStateObj || new TextareaState(panels); + + if (!currState) { + return false; + } + if (mode == "moving") { + if (!lastState) { + lastState = currState; + } + return; + } + if (lastState) { + if (undoStack[stackPtr - 1].text != lastState.text) { + undoStack[stackPtr++] = lastState; + } + lastState = null; + } + undoStack[stackPtr++] = currState; + undoStack[stackPtr + 1] = null; + if (callback) { + callback(); + } + }; + + var handleCtrlYZ = function (event) { + + var handled = false; + + if (event.ctrlKey || event.metaKey) { + + // IE and Opera do not support charCode. + var keyCode = event.charCode || event.keyCode; + var keyCodeChar = String.fromCharCode(keyCode); + + switch (keyCodeChar) { + + case "y": + undoObj.redo(); + handled = true; + break; + + case "z": + if (!event.shiftKey) { + undoObj.undo(); + } + else { + undoObj.redo(); + } + handled = true; + break; + } + } + + if (handled) { + if (event.preventDefault) { + event.preventDefault(); + } + if (window.event) { + window.event.returnValue = false; + } + return; + } + }; + + // Set the mode depending on what is going on in the input area. + var handleModeChange = function (event) { + + if (!event.ctrlKey && !event.metaKey) { + + var keyCode = event.keyCode; + + if ((keyCode >= 33 && keyCode <= 40) || (keyCode >= 63232 && keyCode <= 63235)) { + // 33 - 40: page up/dn and arrow keys + // 63232 - 63235: page up/dn and arrow keys on safari + setMode("moving"); + } + else if (keyCode == 8 || keyCode == 46 || keyCode == 127) { + // 8: backspace + // 46: delete + // 127: delete + setMode("deleting"); + } + else if (keyCode == 13) { + // 13: Enter + setMode("newlines"); + } + else if (keyCode == 27) { + // 27: escape + setMode("escape"); + } + else if ((keyCode < 16 || keyCode > 20) && keyCode != 91) { + // 16-20 are shift, etc. + // 91: left window key + // I think this might be a little messed up since there are + // a lot of nonprinting keys above 20. + setMode("typing"); + } + } + }; + + var setEventHandlers = function () { + util.addEvent(panels.input, "keypress", function (event) { + // keyCode 89: y + // keyCode 90: z + if ((event.ctrlKey || event.metaKey) && (event.keyCode == 89 || event.keyCode == 90)) { + event.preventDefault(); + } + }); + + var handlePaste = function () { + if (uaSniffed.isIE || (inputStateObj && inputStateObj.text != panels.input.value)) { + if (timer == undefined) { + mode = "paste"; + saveState(); + refreshState(); + } + } + }; + + util.addEvent(panels.input, "keydown", handleCtrlYZ); + util.addEvent(panels.input, "keydown", handleModeChange); + util.addEvent(panels.input, "mousedown", function () { + setMode("moving"); + }); + + panels.input.onpaste = handlePaste; + panels.input.ondrop = handlePaste; + }; + + var init = function () { + setEventHandlers(); + refreshState(true); + saveState(); + }; + + init(); + } + + // end of UndoManager + + // The input textarea state/contents. + // This is used to implement undo/redo by the undo manager. + function TextareaState(panels, isInitialState) { + + // Aliases + var stateObj = this; + var inputArea = panels.input; + this.init = function () { + if (!util.isVisible(inputArea)) { + return; + } + if (!isInitialState && doc.activeElement && doc.activeElement !== inputArea) { // this happens when tabbing out of the input box + return; + } + + this.setInputAreaSelectionStartEnd(); + this.scrollTop = inputArea.scrollTop; + if (!this.text && inputArea.selectionStart || inputArea.selectionStart === 0) { + this.text = inputArea.value; + } + + } + + // Sets the selected text in the input box after we've performed an + // operation. + this.setInputAreaSelection = function () { + + if (!util.isVisible(inputArea)) { + return; + } + + if (inputArea.selectionStart !== undefined && !uaSniffed.isOpera) { + + inputArea.focus(); + inputArea.selectionStart = stateObj.start; + inputArea.selectionEnd = stateObj.end; + inputArea.scrollTop = stateObj.scrollTop; + } + else if (doc.selection) { + + if (doc.activeElement && doc.activeElement !== inputArea) { + return; + } + + inputArea.focus(); + var range = inputArea.createTextRange(); + range.moveStart("character", -inputArea.value.length); + range.moveEnd("character", -inputArea.value.length); + range.moveEnd("character", stateObj.end); + range.moveStart("character", stateObj.start); + range.select(); + } + }; + + this.setInputAreaSelectionStartEnd = function () { + + if (!panels.ieCachedRange && (inputArea.selectionStart || inputArea.selectionStart === 0)) { + + stateObj.start = inputArea.selectionStart; + stateObj.end = inputArea.selectionEnd; + } + else if (doc.selection) { + + stateObj.text = util.fixEolChars(inputArea.value); + + // IE loses the selection in the textarea when buttons are + // clicked. On IE we cache the selection. Here, if something is cached, + // we take it. + var range = panels.ieCachedRange || doc.selection.createRange(); + + var fixedRange = util.fixEolChars(range.text); + var marker = "\x07"; + var markedRange = marker + fixedRange + marker; + range.text = markedRange; + var inputText = util.fixEolChars(inputArea.value); + + range.moveStart("character", -markedRange.length); + range.text = fixedRange; + + stateObj.start = inputText.indexOf(marker); + stateObj.end = inputText.lastIndexOf(marker) - marker.length; + + var len = stateObj.text.length - util.fixEolChars(inputArea.value).length; + + if (len) { + range.moveStart("character", -fixedRange.length); + while (len--) { + fixedRange += "\n"; + stateObj.end += 1; + } + range.text = fixedRange; + } + + if (panels.ieCachedRange) + stateObj.scrollTop = panels.ieCachedScrollTop; // this is set alongside with ieCachedRange + + panels.ieCachedRange = null; + + this.setInputAreaSelection(); + } + }; + + // Restore this state into the input area. + this.restore = function () { + + if (stateObj.text != undefined && stateObj.text != inputArea.value) { + inputArea.value = stateObj.text; + } + this.setInputAreaSelection(); + inputArea.scrollTop = stateObj.scrollTop; + }; + + // Gets a collection of HTML chunks from the inptut textarea. + this.getChunks = function () { + + var chunk = new Chunks(); + chunk.before = util.fixEolChars(stateObj.text.substring(0, stateObj.start)); + chunk.startTag = ""; + chunk.selection = util.fixEolChars(stateObj.text.substring(stateObj.start, stateObj.end)); + chunk.endTag = ""; + chunk.after = util.fixEolChars(stateObj.text.substring(stateObj.end)); + chunk.scrollTop = stateObj.scrollTop; + + return chunk; + }; + + // Sets the TextareaState properties given a chunk of markdown. + this.setChunks = function (chunk) { + + chunk.before = chunk.before + chunk.startTag; + chunk.after = chunk.endTag + chunk.after; + + this.start = chunk.before.length; + this.end = chunk.before.length + chunk.selection.length; + this.text = chunk.before + chunk.selection + chunk.after; + this.scrollTop = chunk.scrollTop; + }; + this.init(); + }; + + function PreviewManager(converter, panels, previewRefreshCallback) { + + var managerObj = this; + var timeout; + var elapsedTime; + var oldInputText; + var maxDelay = 3000; + var startType = "delayed"; // The other legal value is "manual" + + // Adds event listeners to elements + var setupEvents = function (inputElem, listener) { + + util.addEvent(inputElem, "input", listener); + inputElem.onpaste = listener; + inputElem.ondrop = listener; + + util.addEvent(inputElem, "keypress", listener); + util.addEvent(inputElem, "keydown", listener); + }; + + var getDocScrollTop = function () { + + var result = 0; + + if (window.innerHeight) { + result = window.pageYOffset; + } + else + if (doc.documentElement && doc.documentElement.scrollTop) { + result = doc.documentElement.scrollTop; + } + else + if (doc.body) { + result = doc.body.scrollTop; + } + + return result; + }; + + var makePreviewHtml = function () { + + // If there is no registered preview panel + // there is nothing to do. + if (!panels.preview) + return; + + + var text = panels.input.value; + if (text && text == oldInputText) { + return; // Input text hasn't changed. + } + else { + oldInputText = text; + } + + var prevTime = new Date().getTime(); + + text = converter.makeHtml(text); + + // Calculate the processing time of the HTML creation. + // It's used as the delay time in the event listener. + var currTime = new Date().getTime(); + elapsedTime = currTime - prevTime; + + pushPreviewHtml(text); + }; + + // setTimeout is already used. Used as an event listener. + var applyTimeout = function () { + + if (timeout) { + clearTimeout(timeout); + timeout = undefined; + } + + if (startType !== "manual") { + + var delay = 0; + + if (startType === "delayed") { + delay = elapsedTime; + } + + if (delay > maxDelay) { + delay = maxDelay; + } + timeout = setTimeout(makePreviewHtml, delay); + } + }; + + var getScaleFactor = function (panel) { + if (panel.scrollHeight <= panel.clientHeight) { + return 1; + } + return panel.scrollTop / (panel.scrollHeight - panel.clientHeight); + }; + + var setPanelScrollTops = function () { + if (panels.preview) { + panels.preview.scrollTop = (panels.preview.scrollHeight - panels.preview.clientHeight) * getScaleFactor(panels.preview); + } + }; + + this.refresh = function (requiresRefresh) { + + if (requiresRefresh) { + oldInputText = ""; + makePreviewHtml(); + } + else { + applyTimeout(); + } + }; + + this.processingTime = function () { + return elapsedTime; + }; + + var isFirstTimeFilled = true; + + // IE doesn't let you use innerHTML if the element is contained somewhere in a table + // (which is the case for inline editing) -- in that case, detach the element, set the + // value, and reattach. Yes, that *is* ridiculous. + var ieSafePreviewSet = function (text) { + var preview = panels.preview; + var parent = preview.parentNode; + var sibling = preview.nextSibling; + parent.removeChild(preview); + preview.innerHTML = text; + if (!sibling) + parent.appendChild(preview); + else + parent.insertBefore(preview, sibling); + } + + var nonSuckyBrowserPreviewSet = function (text) { + panels.preview.innerHTML = text; + } + + var previewSetter; + + var previewSet = function (text) { + if (previewSetter) + return previewSetter(text); + + try { + nonSuckyBrowserPreviewSet(text); + previewSetter = nonSuckyBrowserPreviewSet; + } catch (e) { + previewSetter = ieSafePreviewSet; + previewSetter(text); + } + }; + + var pushPreviewHtml = function (text) { + + var emptyTop = position.getTop(panels.input) - getDocScrollTop(); + + if (panels.preview) { + previewSet(text); + previewRefreshCallback(); + } + + setPanelScrollTops(); + + if (isFirstTimeFilled) { + isFirstTimeFilled = false; + return; + } + + var fullTop = position.getTop(panels.input) - getDocScrollTop(); + + if (uaSniffed.isIE) { + setTimeout(function () { + window.scrollBy(0, fullTop - emptyTop); + }, 0); + } + else { + window.scrollBy(0, fullTop - emptyTop); + } + }; + + var init = function () { + + setupEvents(panels.input, applyTimeout); + makePreviewHtml(); + + if (panels.preview) { + panels.preview.scrollTop = 0; + } + }; + + init(); + }; + + + // This simulates a modal dialog box and asks for the URL when you + // click the hyperlink or image buttons. + // + // text: The html for the input box. + // defaultInputText: The default value that appears in the input box. + // callback: The function which is executed when the prompt is dismissed, either via OK or Cancel. + // It receives a single argument; either the entered text (if OK was chosen) or null (if Cancel + // was chosen). + ui.prompt = function (title, text, defaultInputText, callback) { + + // These variables need to be declared at this level since they are used + // in multiple functions. + var dialog; // The dialog box. + var input; // The text box where you enter the hyperlink. + + + if (defaultInputText === undefined) { + defaultInputText = ""; + } + + // Used as a keydown event handler. Esc dismisses the prompt. + // Key code 27 is ESC. + var checkEscape = function (key) { + var code = (key.charCode || key.keyCode); + if (code === 27) { + close(true); + } + }; + + // Dismisses the hyperlink input box. + // isCancel is true if we don't care about the input text. + // isCancel is false if we are going to keep the text. + var close = function (isCancel) { + util.removeEvent(doc.body, "keydown", checkEscape); + var text = input.value; + + if (isCancel) { + text = null; + } + else { + // Fixes common pasting errors. + text = text.replace(/^http:\/\/(https?|ftp):\/\//, '$1://'); + if (!/^(?:https?|ftp):\/\//.test(text)) + text = 'http://' + text; + } + + $(dialog).modal('hide'); + + callback(text); + return false; + }; + + + + // Create the text input box form/window. + var createDialog = function () { + // + + // The main dialog box. + dialog = doc.createElement("div"); + dialog.className = "modal hide fade"; + dialog.style.display = "none"; + + // The header. + var header = doc.createElement("div"); + header.className = "modal-header"; + header.innerHTML = '×

    ' + title + '

    '; + dialog.appendChild(header); + + // The body. + var body = doc.createElement("div"); + body.className = "modal-body"; + dialog.appendChild(body); + + // The footer. + var footer = doc.createElement("div"); + footer.className = "modal-footer"; + dialog.appendChild(footer); + + // The dialog text. + var question = doc.createElement("p"); + question.innerHTML = text; + question.style.padding = "5px"; + body.appendChild(question); + + // The web form container for the text box and buttons. + var form = doc.createElement("form"), + style = form.style; + form.onsubmit = function () { return close(false); }; + style.padding = "0"; + style.margin = "0"; + body.appendChild(form); + + // The input text box + input = doc.createElement("input"); + input.type = "text"; + input.value = defaultInputText; + style = input.style; + style.display = "block"; + style.width = "80%"; + style.marginLeft = style.marginRight = "auto"; + form.appendChild(input); + + // The ok button + var okButton = doc.createElement("button"); + okButton.className = "btn btn-primary"; + okButton.type = "button"; + okButton.onclick = function () { return close(false); }; + okButton.innerHTML = "OK"; + + // The cancel button + var cancelButton = doc.createElement("button"); + cancelButton.className = "btn btn-primary"; + cancelButton.type = "button"; + cancelButton.onclick = function () { return close(true); }; + cancelButton.innerHTML = "Cancel"; + + footer.appendChild(okButton); + footer.appendChild(cancelButton); + + util.addEvent(doc.body, "keydown", checkEscape); + + doc.body.appendChild(dialog); + + }; + + // Why is this in a zero-length timeout? + // Is it working around a browser bug? + setTimeout(function () { + + createDialog(); + + var defTextLen = defaultInputText.length; + if (input.selectionStart !== undefined) { + input.selectionStart = 0; + input.selectionEnd = defTextLen; + } + else if (input.createTextRange) { + var range = input.createTextRange(); + range.collapse(false); + range.moveStart("character", -defTextLen); + range.moveEnd("character", defTextLen); + range.select(); + } + + $(dialog).on('shown', function () { + input.focus(); + }) + + $(dialog).on('hidden', function () { + dialog.parentNode.removeChild(dialog); + }) + + $(dialog).modal() + + }, 0); + }; + + function UIManager(postfix, panels, undoManager, previewManager, commandManager, helpOptions) { + + var inputBox = panels.input, + buttons = {}; // buttons.undo, buttons.link, etc. The actual DOM elements. + + makeSpritedButtonRow(); + + var keyEvent = "keydown"; + if (uaSniffed.isOpera) { + keyEvent = "keypress"; + } + + util.addEvent(inputBox, keyEvent, function (key) { + + // Check to see if we have a button key and, if so execute the callback. + if ((key.ctrlKey || key.metaKey) && !key.altKey && !key.shiftKey) { + + var keyCode = key.charCode || key.keyCode; + var keyCodeStr = String.fromCharCode(keyCode).toLowerCase(); + + switch (keyCodeStr) { + case "b": + doClick(buttons.bold); + break; + case "i": + doClick(buttons.italic); + break; + case "l": + doClick(buttons.link); + break; + case "q": + doClick(buttons.quote); + break; + case "k": + doClick(buttons.code); + break; + case "g": + doClick(buttons.image); + break; + case "o": + doClick(buttons.olist); + break; + case "u": + doClick(buttons.ulist); + break; + case "h": + doClick(buttons.heading); + break; + case "r": + doClick(buttons.hr); + break; + case "y": + doClick(buttons.redo); + break; + case "z": + if (key.shiftKey) { + doClick(buttons.redo); + } + else { + doClick(buttons.undo); + } + break; + default: + return; + } + + + if (key.preventDefault) { + key.preventDefault(); + } + + if (window.event) { + window.event.returnValue = false; + } + } + }); + + // Auto-indent on shift-enter + util.addEvent(inputBox, "keyup", function (key) { + if (key.shiftKey && !key.ctrlKey && !key.metaKey) { + var keyCode = key.charCode || key.keyCode; + // Character 13 is Enter + if (keyCode === 13) { + var fakeButton = {}; + fakeButton.textOp = bindCommand("doAutoindent"); + doClick(fakeButton); + } + } + }); + + // special handler because IE clears the context of the textbox on ESC + if (uaSniffed.isIE) { + util.addEvent(inputBox, "keydown", function (key) { + var code = key.keyCode; + if (code === 27) { + return false; + } + }); + } + + + // Perform the button's action. + function doClick(button) { + + inputBox.focus(); + + if (button.textOp) { + + if (undoManager) { + undoManager.setCommandMode(); + } + + var state = new TextareaState(panels); + + if (!state) { + return; + } + + var chunks = state.getChunks(); + + // Some commands launch a "modal" prompt dialog. Javascript + // can't really make a modal dialog box and the WMD code + // will continue to execute while the dialog is displayed. + // This prevents the dialog pattern I'm used to and means + // I can't do something like this: + // + // var link = CreateLinkDialog(); + // makeMarkdownLink(link); + // + // Instead of this straightforward method of handling a + // dialog I have to pass any code which would execute + // after the dialog is dismissed (e.g. link creation) + // in a function parameter. + // + // Yes this is awkward and I think it sucks, but there's + // no real workaround. Only the image and link code + // create dialogs and require the function pointers. + var fixupInputArea = function () { + + inputBox.focus(); + + if (chunks) { + state.setChunks(chunks); + } + + state.restore(); + previewManager.refresh(); + }; + + var noCleanup = button.textOp(chunks, fixupInputArea); + + if (!noCleanup) { + fixupInputArea(); + } + + } + + if (button.execute) { + button.execute(undoManager); + } + }; + + function setupButton(button, isEnabled) { + + if (isEnabled) { + button.disabled = false; + + if (!button.isHelp) { + button.onclick = function () { + if (this.onmouseout) { + this.onmouseout(); + } + doClick(this); + return false; + } + } + } + else { + button.disabled = true; + } + } + + function bindCommand(method) { + if (typeof method === "string") + method = commandManager[method]; + return function () { method.apply(commandManager, arguments); } + } + + function makeSpritedButtonRow() { + + var buttonBar = panels.buttonBar; + var buttonRow = document.createElement("div"); + buttonRow.id = "wmd-button-row" + postfix; + buttonRow.className = 'btn-toolbar'; + buttonRow = buttonBar.appendChild(buttonRow); + + var makeButton = function (id, title, icon, textOp, group) { + var button = document.createElement("button"); + button.className = "btn"; + var buttonImage = document.createElement("i"); + buttonImage.className = icon; + button.id = id + postfix; + button.appendChild(buttonImage); + button.title = title; + $(button).tooltip({ placement: 'bottom' }) + if (textOp) + button.textOp = textOp; + setupButton(button, true); + if (group) { + group.appendChild(button); + } else { + buttonRow.appendChild(button); + } + return button; + }; + var makeGroup = function (num) { + var group = document.createElement("div"); + group.className = "btn-group wmd-button-group" + num; + group.id = "wmd-button-group" + num + postfix; + buttonRow.appendChild(group); + return group + } + + group1 = makeGroup(1); + buttons.bold = makeButton("wmd-bold-button", "Bold - Ctrl+B", "icon-drop", bindCommand("doBold"), group1); + buttons.italic = makeButton("wmd-italic-button", "Italic - Ctrl+I", "icon-font", bindCommand("doItalic"), group1); + + group2 = makeGroup(2); + buttons.link = makeButton("wmd-link-button", "Link - Ctrl+L", "icon-link", bindCommand(function (chunk, postProcessing) { + return this.doLinkOrImage(chunk, postProcessing, false); + }), group2); + buttons.quote = makeButton("wmd-quote-button", "Blockquote - Ctrl+Q", "icon-quote", bindCommand("doBlockquote"), group2); + buttons.code = makeButton("wmd-code-button", "Code Sample - Ctrl+K", "icon-code", bindCommand("doCode"), group2); + buttons.image = makeButton("wmd-image-button", "Image - Ctrl+G", "icon-picture", bindCommand(function (chunk, postProcessing) { + return this.doLinkOrImage(chunk, postProcessing, true); + }), group2); + + group3 = makeGroup(3); + buttons.olist = makeButton("wmd-olist-button", "Numbered List - Ctrl+O", "icon-ordered-list", bindCommand(function (chunk, postProcessing) { + this.doList(chunk, postProcessing, true); + }), group3); + buttons.ulist = makeButton("wmd-ulist-button", "Bulleted List - Ctrl+U", "icon-bulleted-list", bindCommand(function (chunk, postProcessing) { + this.doList(chunk, postProcessing, false); + }), group3); + buttons.heading = makeButton("wmd-heading-button", "Heading - Ctrl+H", "icon-shift", bindCommand("doHeading"), group3); + buttons.hr = makeButton("wmd-hr-button", "Horizontal Rule - Ctrl+R", "icon-remove", bindCommand("doHorizontalRule"), group3); + + group4 = makeGroup(4); + buttons.undo = makeButton("wmd-undo-button", "Undo - Ctrl+Z", "icon-undo", null, group4); + buttons.undo.execute = function (manager) { if (manager) manager.undo(); }; + + var redoTitle = /win/.test(nav.platform.toLowerCase()) ? + "Redo - Ctrl+Y" : + "Redo - Ctrl+Shift+Z"; // mac and other non-Windows platforms + + buttons.redo = makeButton("wmd-redo-button", redoTitle, "icon-share-alt", null, group4); + buttons.redo.execute = function (manager) { if (manager) manager.redo(); }; + + if (helpOptions) { + group5 = makeGroup(5); + group5.className = group5.className + " pull-right"; + var helpButton = document.createElement("button"); + var helpButtonImage = document.createElement("i"); + helpButtonImage.className = "icon-question-sign"; + helpButton.appendChild(helpButtonImage); + helpButton.className = "btn"; + helpButton.id = "wmd-help-button" + postfix; + helpButton.isHelp = true; + helpButton.title = helpOptions.title || defaultHelpHoverTitle; + $(helpButton).tooltip({ placement: 'bottom' }) + helpButton.onclick = helpOptions.handler; + + setupButton(helpButton, true); + group5.appendChild(helpButton); + buttons.help = helpButton; + } + + setUndoRedoButtonStates(); + } + + function setUndoRedoButtonStates() { + if (undoManager) { + setupButton(buttons.undo, undoManager.canUndo()); + setupButton(buttons.redo, undoManager.canRedo()); + } + }; + + this.setUndoRedoButtonStates = setUndoRedoButtonStates; + + } + + function CommandManager(pluginHooks) { + this.hooks = pluginHooks; + } + + var commandProto = CommandManager.prototype; + + // The markdown symbols - 4 spaces = code, > = blockquote, etc. + commandProto.prefixes = "(?:\\s{4,}|\\s*>|\\s*-\\s+|\\s*\\d+\\.|=|\\+|-|_|\\*|#|\\s*\\[[^\n]]+\\]:)"; + + // Remove markdown symbols from the chunk selection. + commandProto.unwrap = function (chunk) { + var txt = new re("([^\\n])\\n(?!(\\n|" + this.prefixes + "))", "g"); + chunk.selection = chunk.selection.replace(txt, "$1 $2"); + }; + + commandProto.wrap = function (chunk, len) { + this.unwrap(chunk); + var regex = new re("(.{1," + len + "})( +|$\\n?)", "gm"), + that = this; + + chunk.selection = chunk.selection.replace(regex, function (line, marked) { + if (new re("^" + that.prefixes, "").test(line)) { + return line; + } + return marked + "\n"; + }); + + chunk.selection = chunk.selection.replace(/\s+$/, ""); + }; + + commandProto.doBold = function (chunk, postProcessing) { + return this.doBorI(chunk, postProcessing, 2, "strong text"); + }; + + commandProto.doItalic = function (chunk, postProcessing) { + return this.doBorI(chunk, postProcessing, 1, "emphasized text"); + }; + + // chunk: The selected region that will be enclosed with */** + // nStars: 1 for italics, 2 for bold + // insertText: If you just click the button without highlighting text, this gets inserted + commandProto.doBorI = function (chunk, postProcessing, nStars, insertText) { + + // Get rid of whitespace and fixup newlines. + chunk.trimWhitespace(); + chunk.selection = chunk.selection.replace(/\n{2,}/g, "\n"); + + // Look for stars before and after. Is the chunk already marked up? + // note that these regex matches cannot fail + var starsBefore = /(\**$)/.exec(chunk.before)[0]; + var starsAfter = /(^\**)/.exec(chunk.after)[0]; + + var prevStars = Math.min(starsBefore.length, starsAfter.length); + + // Remove stars if we have to since the button acts as a toggle. + if ((prevStars >= nStars) && (prevStars != 2 || nStars != 1)) { + chunk.before = chunk.before.replace(re("[*]{" + nStars + "}$", ""), ""); + chunk.after = chunk.after.replace(re("^[*]{" + nStars + "}", ""), ""); + } + else if (!chunk.selection && starsAfter) { + // It's not really clear why this code is necessary. It just moves + // some arbitrary stuff around. + chunk.after = chunk.after.replace(/^([*_]*)/, ""); + chunk.before = chunk.before.replace(/(\s?)$/, ""); + var whitespace = re.$1; + chunk.before = chunk.before + starsAfter + whitespace; + } + else { + + // In most cases, if you don't have any selected text and click the button + // you'll get a selected, marked up region with the default text inserted. + if (!chunk.selection && !starsAfter) { + chunk.selection = insertText; + } + + // Add the true markup. + var markup = nStars <= 1 ? "*" : "**"; // shouldn't the test be = ? + chunk.before = chunk.before + markup; + chunk.after = markup + chunk.after; + } + + return; + }; + + commandProto.stripLinkDefs = function (text, defsToAdd) { + + text = text.replace(/^[ ]{0,3}\[(\d+)\]:[ \t]*\n?[ \t]*?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|$)/gm, + function (totalMatch, id, link, newlines, title) { + defsToAdd[id] = totalMatch.replace(/\s*$/, ""); + if (newlines) { + // Strip the title and return that separately. + defsToAdd[id] = totalMatch.replace(/["(](.+?)[")]$/, ""); + return newlines + title; + } + return ""; + }); + + return text; + }; + + commandProto.addLinkDef = function (chunk, linkDef) { + + var refNumber = 0; // The current reference number + var defsToAdd = {}; // + // Start with a clean slate by removing all previous link definitions. + chunk.before = this.stripLinkDefs(chunk.before, defsToAdd); + chunk.selection = this.stripLinkDefs(chunk.selection, defsToAdd); + chunk.after = this.stripLinkDefs(chunk.after, defsToAdd); + + var defs = ""; + var regex = /(\[)((?:\[[^\]]*\]|[^\[\]])*)(\][ ]?(?:\n[ ]*)?\[)(\d+)(\])/g; + + var addDefNumber = function (def) { + refNumber++; + def = def.replace(/^[ ]{0,3}\[(\d+)\]:/, " [" + refNumber + "]:"); + defs += "\n" + def; + }; + + // note that + // a) the recursive call to getLink cannot go infinite, because by definition + // of regex, inner is always a proper substring of wholeMatch, and + // b) more than one level of nesting is neither supported by the regex + // nor making a lot of sense (the only use case for nesting is a linked image) + var getLink = function (wholeMatch, before, inner, afterInner, id, end) { + inner = inner.replace(regex, getLink); + if (defsToAdd[id]) { + addDefNumber(defsToAdd[id]); + return before + inner + afterInner + refNumber + end; + } + return wholeMatch; + }; + + chunk.before = chunk.before.replace(regex, getLink); + + if (linkDef) { + addDefNumber(linkDef); + } + else { + chunk.selection = chunk.selection.replace(regex, getLink); + } + + var refOut = refNumber; + + chunk.after = chunk.after.replace(regex, getLink); + + if (chunk.after) { + chunk.after = chunk.after.replace(/\n*$/, ""); + } + if (!chunk.after) { + chunk.selection = chunk.selection.replace(/\n*$/, ""); + } + + chunk.after += "\n\n" + defs; + + return refOut; + }; + + // takes the line as entered into the add link/as image dialog and makes + // sure the URL and the optinal title are "nice". + function properlyEncoded(linkdef) { + return linkdef.replace(/^\s*(.*?)(?:\s+"(.+)")?\s*$/, function (wholematch, link, title) { + link = link.replace(/\?.*$/, function (querypart) { + return querypart.replace(/\+/g, " "); // in the query string, a plus and a space are identical + }); + link = decodeURIComponent(link); // unencode first, to prevent double encoding + link = encodeURI(link).replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29'); + link = link.replace(/\?.*$/, function (querypart) { + return querypart.replace(/\+/g, "%2b"); // since we replaced plus with spaces in the query part, all pluses that now appear where originally encoded + }); + if (title) { + title = title.trim ? title.trim() : title.replace(/^\s*/, "").replace(/\s*$/, ""); + title = $.trim(title).replace(/"/g, "quot;").replace(/\(/g, "(").replace(/\)/g, ")").replace(//g, ">"); + } + return title ? link + ' "' + title + '"' : link; + }); + } + + commandProto.doLinkOrImage = function (chunk, postProcessing, isImage) { + + chunk.trimWhitespace(); + chunk.findTags(/\s*!?\[/, /\][ ]?(?:\n[ ]*)?(\[.*?\])?/); + var background; + + if (chunk.endTag.length > 1 && chunk.startTag.length > 0) { + + chunk.startTag = chunk.startTag.replace(/!?\[/, ""); + chunk.endTag = ""; + this.addLinkDef(chunk, null); + + } + else { + + // We're moving start and end tag back into the selection, since (as we're in the else block) we're not + // *removing* a link, but *adding* one, so whatever findTags() found is now back to being part of the + // link text. linkEnteredCallback takes care of escaping any brackets. + chunk.selection = chunk.startTag + chunk.selection + chunk.endTag; + chunk.startTag = chunk.endTag = ""; + + if (/\n\n/.test(chunk.selection)) { + this.addLinkDef(chunk, null); + return; + } + var that = this; + // The function to be executed when you enter a link and press OK or Cancel. + // Marks up the link and adds the ref. + var linkEnteredCallback = function (link) { + + if (link !== null) { + // ( $1 + // [^\\] anything that's not a backslash + // (?:\\\\)* an even number (this includes zero) of backslashes + // ) + // (?= followed by + // [[\]] an opening or closing bracket + // ) + // + // In other words, a non-escaped bracket. These have to be escaped now to make sure they + // don't count as the end of the link or similar. + // Note that the actual bracket has to be a lookahead, because (in case of to subsequent brackets), + // the bracket in one match may be the "not a backslash" character in the next match, so it + // should not be consumed by the first match. + // The "prepend a space and finally remove it" steps makes sure there is a "not a backslash" at the + // start of the string, so this also works if the selection begins with a bracket. We cannot solve + // this by anchoring with ^, because in the case that the selection starts with two brackets, this + // would mean a zero-width match at the start. Since zero-width matches advance the string position, + // the first bracket could then not act as the "not a backslash" for the second. + chunk.selection = (" " + chunk.selection).replace(/([^\\](?:\\\\)*)(?=[[\]])/g, "$1\\").substr(1); + + var linkDef = " [999]: " + properlyEncoded(link); + + var num = that.addLinkDef(chunk, linkDef); + chunk.startTag = isImage ? "![" : "["; + chunk.endTag = "][" + num + "]"; + + if (!chunk.selection) { + if (isImage) { + chunk.selection = "enter image description here"; + } + else { + chunk.selection = "enter link description here"; + } + } + } + postProcessing(); + }; + + + if (isImage) { + if (!this.hooks.insertImageDialog(linkEnteredCallback)) + ui.prompt('Insert Image', imageDialogText, imageDefaultText, linkEnteredCallback); + } + else { + ui.prompt('Insert Link', linkDialogText, linkDefaultText, linkEnteredCallback); + } + return true; + } + }; + + // When making a list, hitting shift-enter will put your cursor on the next line + // at the current indent level. + commandProto.doAutoindent = function (chunk, postProcessing) { + + var commandMgr = this, + fakeSelection = false; + + chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]*\n$/, "\n\n"); + chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}>[ \t]*\n$/, "\n\n"); + chunk.before = chunk.before.replace(/(\n|^)[ \t]+\n$/, "\n\n"); + + // There's no selection, end the cursor wasn't at the end of the line: + // The user wants to split the current list item / code line / blockquote line + // (for the latter it doesn't really matter) in two. Temporarily select the + // (rest of the) line to achieve this. + if (!chunk.selection && !/^[ \t]*(?:\n|$)/.test(chunk.after)) { + chunk.after = chunk.after.replace(/^[^\n]*/, function (wholeMatch) { + chunk.selection = wholeMatch; + return ""; + }); + fakeSelection = true; + } + + if (/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]+.*\n$/.test(chunk.before)) { + if (commandMgr.doList) { + commandMgr.doList(chunk); + } + } + if (/(\n|^)[ ]{0,3}>[ \t]+.*\n$/.test(chunk.before)) { + if (commandMgr.doBlockquote) { + commandMgr.doBlockquote(chunk); + } + } + if (/(\n|^)(\t|[ ]{4,}).*\n$/.test(chunk.before)) { + if (commandMgr.doCode) { + commandMgr.doCode(chunk); + } + } + + if (fakeSelection) { + chunk.after = chunk.selection + chunk.after; + chunk.selection = ""; + } + }; + + commandProto.doBlockquote = function (chunk, postProcessing) { + + chunk.selection = chunk.selection.replace(/^(\n*)([^\r]+?)(\n*)$/, + function (totalMatch, newlinesBefore, text, newlinesAfter) { + chunk.before += newlinesBefore; + chunk.after = newlinesAfter + chunk.after; + return text; + }); + + chunk.before = chunk.before.replace(/(>[ \t]*)$/, + function (totalMatch, blankLine) { + chunk.selection = blankLine + chunk.selection; + return ""; + }); + + chunk.selection = chunk.selection.replace(/^(\s|>)+$/, ""); + chunk.selection = chunk.selection || "Blockquote"; + + // The original code uses a regular expression to find out how much of the + // text *directly before* the selection already was a blockquote: + + /* + if (chunk.before) { + chunk.before = chunk.before.replace(/\n?$/, "\n"); + } + chunk.before = chunk.before.replace(/(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*$)/, + function (totalMatch) { + chunk.startTag = totalMatch; + return ""; + }); + */ + + // This comes down to: + // Go backwards as many lines a possible, such that each line + // a) starts with ">", or + // b) is almost empty, except for whitespace, or + // c) is preceeded by an unbroken chain of non-empty lines + // leading up to a line that starts with ">" and at least one more character + // and in addition + // d) at least one line fulfills a) + // + // Since this is essentially a backwards-moving regex, it's susceptible to + // catstrophic backtracking and can cause the browser to hang; + // see e.g. http://meta.stackoverflow.com/questions/9807. + // + // Hence we replaced this by a simple state machine that just goes through the + // lines and checks for a), b), and c). + + var match = "", + leftOver = "", + line; + if (chunk.before) { + var lines = chunk.before.replace(/\n$/, "").split("\n"); + var inChain = false; + for (var i = 0; i < lines.length; i++) { + var good = false; + line = lines[i]; + inChain = inChain && line.length > 0; // c) any non-empty line continues the chain + if (/^>/.test(line)) { // a) + good = true; + if (!inChain && line.length > 1) // c) any line that starts with ">" and has at least one more character starts the chain + inChain = true; + } else if (/^[ \t]*$/.test(line)) { // b) + good = true; + } else { + good = inChain; // c) the line is not empty and does not start with ">", so it matches if and only if we're in the chain + } + if (good) { + match += line + "\n"; + } else { + leftOver += match + line; + match = "\n"; + } + } + if (!/(^|\n)>/.test(match)) { // d) + leftOver += match; + match = ""; + } + } + + chunk.startTag = match; + chunk.before = leftOver; + + // end of change + + if (chunk.after) { + chunk.after = chunk.after.replace(/^\n?/, "\n"); + } + + chunk.after = chunk.after.replace(/^(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*)/, + function (totalMatch) { + chunk.endTag = totalMatch; + return ""; + } + ); + + var replaceBlanksInTags = function (useBracket) { + + var replacement = useBracket ? "> " : ""; + + if (chunk.startTag) { + chunk.startTag = chunk.startTag.replace(/\n((>|\s)*)\n$/, + function (totalMatch, markdown) { + return "\n" + markdown.replace(/^[ ]{0,3}>?[ \t]*$/gm, replacement) + "\n"; + }); + } + if (chunk.endTag) { + chunk.endTag = chunk.endTag.replace(/^\n((>|\s)*)\n/, + function (totalMatch, markdown) { + return "\n" + markdown.replace(/^[ ]{0,3}>?[ \t]*$/gm, replacement) + "\n"; + }); + } + }; + + if (/^(?![ ]{0,3}>)/m.test(chunk.selection)) { + this.wrap(chunk, SETTINGS.lineLength - 2); + chunk.selection = chunk.selection.replace(/^/gm, "> "); + replaceBlanksInTags(true); + chunk.skipLines(); + } else { + chunk.selection = chunk.selection.replace(/^[ ]{0,3}> ?/gm, ""); + this.unwrap(chunk); + replaceBlanksInTags(false); + + if (!/^(\n|^)[ ]{0,3}>/.test(chunk.selection) && chunk.startTag) { + chunk.startTag = chunk.startTag.replace(/\n{0,2}$/, "\n\n"); + } + + if (!/(\n|^)[ ]{0,3}>.*$/.test(chunk.selection) && chunk.endTag) { + chunk.endTag = chunk.endTag.replace(/^\n{0,2}/, "\n\n"); + } + } + + chunk.selection = this.hooks.postBlockquoteCreation(chunk.selection); + + if (!/\n/.test(chunk.selection)) { + chunk.selection = chunk.selection.replace(/^(> *)/, + function (wholeMatch, blanks) { + chunk.startTag += blanks; + return ""; + }); + } + }; + + commandProto.doCode = function (chunk, postProcessing) { + + var hasTextBefore = /\S[ ]*$/.test(chunk.before); + var hasTextAfter = /^[ ]*\S/.test(chunk.after); + + // Use 'four space' markdown if the selection is on its own + // line or is multiline. + if ((!hasTextAfter && !hasTextBefore) || /\n/.test(chunk.selection)) { + + chunk.before = chunk.before.replace(/[ ]{4}$/, + function (totalMatch) { + chunk.selection = totalMatch + chunk.selection; + return ""; + }); + + var nLinesBack = 1; + var nLinesForward = 1; + + if (/(\n|^)(\t|[ ]{4,}).*\n$/.test(chunk.before)) { + nLinesBack = 0; + } + if (/^\n(\t|[ ]{4,})/.test(chunk.after)) { + nLinesForward = 0; + } + + chunk.skipLines(nLinesBack, nLinesForward); + + if (!chunk.selection) { + chunk.startTag = " "; + chunk.selection = "enter code here"; + } + else { + if (/^[ ]{0,3}\S/m.test(chunk.selection)) { + if (/\n/.test(chunk.selection)) + chunk.selection = chunk.selection.replace(/^/gm, " "); + else // if it's not multiline, do not select the four added spaces; this is more consistent with the doList behavior + chunk.before += " "; + } + else { + chunk.selection = chunk.selection.replace(/^[ ]{4}/gm, ""); + } + } + } + else { + // Use backticks (`) to delimit the code block. + + chunk.trimWhitespace(); + chunk.findTags(/`/, /`/); + + if (!chunk.startTag && !chunk.endTag) { + chunk.startTag = chunk.endTag = "`"; + if (!chunk.selection) { + chunk.selection = "enter code here"; + } + } + else if (chunk.endTag && !chunk.startTag) { + chunk.before += chunk.endTag; + chunk.endTag = ""; + } + else { + chunk.startTag = chunk.endTag = ""; + } + } + }; + + commandProto.doList = function (chunk, postProcessing, isNumberedList) { + + // These are identical except at the very beginning and end. + // Should probably use the regex extension function to make this clearer. + var previousItemsRegex = /(\n|^)(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*$/; + var nextItemsRegex = /^\n*(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*/; + + // The default bullet is a dash but others are possible. + // This has nothing to do with the particular HTML bullet, + // it's just a markdown bullet. + var bullet = "-"; + + // The number in a numbered list. + var num = 1; + + // Get the item prefix - e.g. " 1. " for a numbered list, " - " for a bulleted list. + var getItemPrefix = function () { + var prefix; + if (isNumberedList) { + prefix = " " + num + ". "; + num++; + } + else { + prefix = " " + bullet + " "; + } + return prefix; + }; + + // Fixes the prefixes of the other list items. + var getPrefixedItem = function (itemText) { + + // The numbering flag is unset when called by autoindent. + if (isNumberedList === undefined) { + isNumberedList = /^\s*\d/.test(itemText); + } + + // Renumber/bullet the list element. + itemText = itemText.replace(/^[ ]{0,3}([*+-]|\d+[.])\s/gm, + function (_) { + return getItemPrefix(); + }); + + return itemText; + }; + + chunk.findTags(/(\n|^)*[ ]{0,3}([*+-]|\d+[.])\s+/, null); + + if (chunk.before && !/\n$/.test(chunk.before) && !/^\n/.test(chunk.startTag)) { + chunk.before += chunk.startTag; + chunk.startTag = ""; + } + + if (chunk.startTag) { + + var hasDigits = /\d+[.]/.test(chunk.startTag); + chunk.startTag = ""; + chunk.selection = chunk.selection.replace(/\n[ ]{4}/g, "\n"); + this.unwrap(chunk); + chunk.skipLines(); + + if (hasDigits) { + // Have to renumber the bullet points if this is a numbered list. + chunk.after = chunk.after.replace(nextItemsRegex, getPrefixedItem); + } + if (isNumberedList == hasDigits) { + return; + } + } + + var nLinesUp = 1; + + chunk.before = chunk.before.replace(previousItemsRegex, + function (itemText) { + if (/^\s*([*+-])/.test(itemText)) { + bullet = re.$1; + } + nLinesUp = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0; + return getPrefixedItem(itemText); + }); + + if (!chunk.selection) { + chunk.selection = "List item"; + } + + var prefix = getItemPrefix(); + + var nLinesDown = 1; + + chunk.after = chunk.after.replace(nextItemsRegex, + function (itemText) { + nLinesDown = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0; + return getPrefixedItem(itemText); + }); + + chunk.trimWhitespace(true); + chunk.skipLines(nLinesUp, nLinesDown, true); + chunk.startTag = prefix; + var spaces = prefix.replace(/./g, " "); + this.wrap(chunk, SETTINGS.lineLength - spaces.length); + chunk.selection = chunk.selection.replace(/\n/g, "\n" + spaces); + + }; + + commandProto.doHeading = function (chunk, postProcessing) { + + // Remove leading/trailing whitespace and reduce internal spaces to single spaces. + chunk.selection = chunk.selection.replace(/\s+/g, " "); + chunk.selection = chunk.selection.replace(/(^\s+|\s+$)/g, ""); + + // If we clicked the button with no selected text, we just + // make a level 2 hash header around some default text. + if (!chunk.selection) { + chunk.startTag = "## "; + chunk.selection = "Heading"; + chunk.endTag = " ##"; + return; + } + + var headerLevel = 0; // The existing header level of the selected text. + + // Remove any existing hash heading markdown and save the header level. + chunk.findTags(/#+[ ]*/, /[ ]*#+/); + if (/#+/.test(chunk.startTag)) { + headerLevel = re.lastMatch.length; + } + chunk.startTag = chunk.endTag = ""; + + // Try to get the current header level by looking for - and = in the line + // below the selection. + chunk.findTags(null, /\s?(-+|=+)/); + if (/=+/.test(chunk.endTag)) { + headerLevel = 1; + } + if (/-+/.test(chunk.endTag)) { + headerLevel = 2; + } + + // Skip to the next line so we can create the header markdown. + chunk.startTag = chunk.endTag = ""; + chunk.skipLines(1, 1); + + // We make a level 2 header if there is no current header. + // If there is a header level, we substract one from the header level. + // If it's already a level 1 header, it's removed. + var headerLevelToCreate = headerLevel == 0 ? 2 : headerLevel - 1; + + if (headerLevelToCreate > 0) { + + // The button only creates level 1 and 2 underline headers. + // Why not have it iterate over hash header levels? Wouldn't that be easier and cleaner? + var headerChar = headerLevelToCreate >= 2 ? "-" : "="; + var len = chunk.selection.length; + if (len > SETTINGS.lineLength) { + len = SETTINGS.lineLength; + } + chunk.endTag = "\n"; + while (len--) { + chunk.endTag += headerChar; + } + } + }; + + commandProto.doHorizontalRule = function (chunk, postProcessing) { + chunk.startTag = "----------\n"; + chunk.selection = ""; + chunk.skipLines(2, 1, true); + } + + })(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/markdown/markdown.sanitizer.js b/src/Umbraco.Web.UI.Client/lib/markdown/markdown.sanitizer.js index 6076514962..72aaac70ef 100644 --- a/src/Umbraco.Web.UI.Client/lib/markdown/markdown.sanitizer.js +++ b/src/Umbraco.Web.UI.Client/lib/markdown/markdown.sanitizer.js @@ -1,111 +1,111 @@ -(function () { - var output, Converter; - if (typeof exports === "object" && typeof require === "function") { // we're in a CommonJS (e.g. Node.js) module - output = exports; - Converter = require("./Markdown.Converter").Converter; - } else { - output = window.Markdown; - Converter = output.Converter; - } - - output.getSanitizingConverter = function () { - var converter = new Converter(); - converter.hooks.chain("postConversion", sanitizeHtml); - converter.hooks.chain("postConversion", balanceTags); - return converter; - } - - function sanitizeHtml(html) { - return html.replace(/<[^>]*>?/gi, sanitizeTag); - } - - // (tags that can be opened/closed) | (tags that stand alone) - var basic_tag_whitelist = /^(<\/?(b|blockquote|code|del|dd|dl|dt|em|h1|h2|h3|i|kbd|li|ol|p|s|sup|sub|strong|strike|ul)>|<(br|hr)\s?\/?>)$/i; - // | - var a_white = /^(]+")?\s?>|<\/a>)$/i; - - // ]*")?(\stitle="[^"<>]*")?\s?\/?>)$/i; - - //
    |
    for twitter bootstrap - var pre_white = /^(|<\/pre>)$/i; - - function sanitizeTag(tag) { - if (tag.match(basic_tag_whitelist) || tag.match(a_white) || tag.match(img_white) || tag.match(pre_white)) - return tag; - else - return ""; - } - - /// - /// attempt to balance HTML tags in the html string - /// by removing any unmatched opening or closing tags - /// IMPORTANT: we *assume* HTML has *already* been - /// sanitized and is safe/sane before balancing! - /// - /// adapted from CODESNIPPET: A8591DBA-D1D3-11DE-947C-BA5556D89593 - /// - function balanceTags(html) { - - if (html == "") - return ""; - - var re = /<\/?\w+[^>]*(\s|$|>)/g; - // convert everything to lower case; this makes - // our case insensitive comparisons easier - var tags = html.toLowerCase().match(re); - - // no HTML tags present? nothing to do; exit now - var tagcount = (tags || []).length; - if (tagcount == 0) - return html; - - var tagname, tag; - var ignoredtags = "



  • "; - var match; - var tagpaired = []; - var tagremove = []; - var needsRemoval = false; - - // loop through matched tags in forward order - for (var ctag = 0; ctag < tagcount; ctag++) { - tagname = tags[ctag].replace(/<\/?(\w+).*/, "$1"); - // skip any already paired tags - // and skip tags in our ignore list; assume they're self-closed - if (tagpaired[ctag] || ignoredtags.search("<" + tagname + ">") > -1) - continue; - - tag = tags[ctag]; - match = -1; - - if (!/^<\//.test(tag)) { - // this is an opening tag - // search forwards (next tags), look for closing tags - for (var ntag = ctag + 1; ntag < tagcount; ntag++) { - if (!tagpaired[ntag] && tags[ntag] == "") { - match = ntag; - break; - } - } - } - - if (match == -1) - needsRemoval = tagremove[ctag] = true; // mark for removal - else - tagpaired[match] = true; // mark paired - } - - if (!needsRemoval) - return html; - - // delete all orphaned tags from the string - - var ctag = 0; - html = html.replace(re, function (match) { - var res = tagremove[ctag] ? "" : match; - ctag++; - return res; - }); - return html; - } -})(); +(function () { + var output, Converter; + if (typeof exports === "object" && typeof require === "function") { // we're in a CommonJS (e.g. Node.js) module + output = exports; + Converter = require("./Markdown.Converter").Converter; + } else { + output = window.Markdown; + Converter = output.Converter; + } + + output.getSanitizingConverter = function () { + var converter = new Converter(); + converter.hooks.chain("postConversion", sanitizeHtml); + converter.hooks.chain("postConversion", balanceTags); + return converter; + } + + function sanitizeHtml(html) { + return html.replace(/<[^>]*>?/gi, sanitizeTag); + } + + // (tags that can be opened/closed) | (tags that stand alone) + var basic_tag_whitelist = /^(<\/?(b|blockquote|code|del|dd|dl|dt|em|h1|h2|h3|i|kbd|li|ol|p|s|sup|sub|strong|strike|ul)>|<(br|hr)\s?\/?>)$/i; + // | + var a_white = /^(]+")?\s?>|<\/a>)$/i; + + // ]*")?(\stitle="[^"<>]*")?\s?\/?>)$/i; + + //
    |
    for twitter bootstrap + var pre_white = /^(|<\/pre>)$/i; + + function sanitizeTag(tag) { + if (tag.match(basic_tag_whitelist) || tag.match(a_white) || tag.match(img_white) || tag.match(pre_white)) + return tag; + else + return ""; + } + + /// + /// attempt to balance HTML tags in the html string + /// by removing any unmatched opening or closing tags + /// IMPORTANT: we *assume* HTML has *already* been + /// sanitized and is safe/sane before balancing! + /// + /// adapted from CODESNIPPET: A8591DBA-D1D3-11DE-947C-BA5556D89593 + /// + function balanceTags(html) { + + if (html == "") + return ""; + + var re = /<\/?\w+[^>]*(\s|$|>)/g; + // convert everything to lower case; this makes + // our case insensitive comparisons easier + var tags = html.toLowerCase().match(re); + + // no HTML tags present? nothing to do; exit now + var tagcount = (tags || []).length; + if (tagcount == 0) + return html; + + var tagname, tag; + var ignoredtags = "



  • "; + var match; + var tagpaired = []; + var tagremove = []; + var needsRemoval = false; + + // loop through matched tags in forward order + for (var ctag = 0; ctag < tagcount; ctag++) { + tagname = tags[ctag].replace(/<\/?(\w+).*/, "$1"); + // skip any already paired tags + // and skip tags in our ignore list; assume they're self-closed + if (tagpaired[ctag] || ignoredtags.search("<" + tagname + ">") > -1) + continue; + + tag = tags[ctag]; + match = -1; + + if (!/^<\//.test(tag)) { + // this is an opening tag + // search forwards (next tags), look for closing tags + for (var ntag = ctag + 1; ntag < tagcount; ntag++) { + if (!tagpaired[ntag] && tags[ntag] == "") { + match = ntag; + break; + } + } + } + + if (match == -1) + needsRemoval = tagremove[ctag] = true; // mark for removal + else + tagpaired[match] = true; // mark paired + } + + if (!needsRemoval) + return html; + + // delete all orphaned tags from the string + + var ctag = 0; + html = html.replace(re, function (match) { + var res = tagremove[ctag] ? "" : match; + ctag++; + return res; + }); + return html; + } +})(); diff --git a/src/Umbraco.Web.UI.Client/lib/markdown/red.css b/src/Umbraco.Web.UI.Client/lib/markdown/red.css index b317f56b86..0a3c3dc792 100644 --- a/src/Umbraco.Web.UI.Client/lib/markdown/red.css +++ b/src/Umbraco.Web.UI.Client/lib/markdown/red.css @@ -1,6 +1,6 @@ -.icon-rs-custom{ - background: red; - display: inline-block; - height: 32px; - width: 32px; +.icon-rs-custom{ + background: red; + display: inline-block; + height: 32px; + width: 32px; }; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/slider/slider.css b/src/Umbraco.Web.UI.Client/lib/slider/slider.css index b527aa8686..8cbc16cf85 100644 --- a/src/Umbraco.Web.UI.Client/lib/slider/slider.css +++ b/src/Umbraco.Web.UI.Client/lib/slider/slider.css @@ -1,138 +1,138 @@ -/*! - * Slider for Bootstrap - * - * Copyright 2012 Stefan Petre - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - */ -.slider { - display: inline-block; - vertical-align: middle; - position: relative; -} -.slider.slider-horizontal { - width: 210px; - height: 20px; -} -.slider.slider-horizontal .slider-track { - height: 10px; - width: 100%; - margin-top: -5px; - top: 50%; - left: 0; -} -.slider.slider-horizontal .slider-selection { - height: 100%; - top: 0; - bottom: 0; -} -.slider.slider-horizontal .slider-handle { - margin-left: -10px; - margin-top: -5px; -} -.slider.slider-horizontal .slider-handle.triangle { - border-width: 0 10px 10px 10px; - width: 0; - height: 0; - border-bottom-color: #0480be; - margin-top: 0; -} -.slider.slider-vertical { - height: 210px; - width: 20px; -} -.slider.slider-vertical .slider-track { - width: 10px; - height: 100%; - margin-left: -5px; - left: 50%; - top: 0; -} -.slider.slider-vertical .slider-selection { - width: 100%; - left: 0; - top: 0; - bottom: 0; -} -.slider.slider-vertical .slider-handle { - margin-left: -5px; - margin-top: -10px; -} -.slider.slider-vertical .slider-handle.triangle { - border-width: 10px 0 10px 10px; - width: 1px; - height: 1px; - border-left-color: #0480be; - margin-left: 0; -} -.slider input { - display: none; -} -.slider .tooltip-inner { - white-space: nowrap; -} -.slider-track { - position: absolute; - cursor: pointer; - background-color: #f7f7f7; - background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); - background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); - background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); - background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.slider-selection { - position: absolute; - background-color: #f7f7f7; - background-image: -moz-linear-gradient(top, #f9f9f9, #f5f5f5); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f9f9f9), to(#f5f5f5)); - background-image: -webkit-linear-gradient(top, #f9f9f9, #f5f5f5); - background-image: -o-linear-gradient(top, #f9f9f9, #f5f5f5); - background-image: linear-gradient(to bottom, #f9f9f9, #f5f5f5); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#fff5f5f5', GradientType=0); - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.slider-handle { - position: absolute; - width: 20px; - height: 20px; - background-color: #0e90d2; - background-image: -moz-linear-gradient(top, #149bdf, #0480be); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); - background-image: -webkit-linear-gradient(top, #149bdf, #0480be); - background-image: -o-linear-gradient(top, #149bdf, #0480be); - background-image: linear-gradient(to bottom, #149bdf, #0480be); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); - -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); - -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); - box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); - opacity: 0.8; - border: 0px solid transparent; -} -.slider-handle.round { - -webkit-border-radius: 20px; - -moz-border-radius: 20px; - border-radius: 20px; -} -.slider-handle.triangle { - background: transparent none; +/*! + * Slider for Bootstrap + * + * Copyright 2012 Stefan Petre + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ +.slider { + display: inline-block; + vertical-align: middle; + position: relative; +} +.slider.slider-horizontal { + width: 210px; + height: 20px; +} +.slider.slider-horizontal .slider-track { + height: 10px; + width: 100%; + margin-top: -5px; + top: 50%; + left: 0; +} +.slider.slider-horizontal .slider-selection { + height: 100%; + top: 0; + bottom: 0; +} +.slider.slider-horizontal .slider-handle { + margin-left: -10px; + margin-top: -5px; +} +.slider.slider-horizontal .slider-handle.triangle { + border-width: 0 10px 10px 10px; + width: 0; + height: 0; + border-bottom-color: #0480be; + margin-top: 0; +} +.slider.slider-vertical { + height: 210px; + width: 20px; +} +.slider.slider-vertical .slider-track { + width: 10px; + height: 100%; + margin-left: -5px; + left: 50%; + top: 0; +} +.slider.slider-vertical .slider-selection { + width: 100%; + left: 0; + top: 0; + bottom: 0; +} +.slider.slider-vertical .slider-handle { + margin-left: -5px; + margin-top: -10px; +} +.slider.slider-vertical .slider-handle.triangle { + border-width: 10px 0 10px 10px; + width: 1px; + height: 1px; + border-left-color: #0480be; + margin-left: 0; +} +.slider input { + display: none; +} +.slider .tooltip-inner { + white-space: nowrap; +} +.slider-track { + position: absolute; + cursor: pointer; + background-color: #f7f7f7; + background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); + background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.slider-selection { + position: absolute; + background-color: #f7f7f7; + background-image: -moz-linear-gradient(top, #f9f9f9, #f5f5f5); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f9f9f9), to(#f5f5f5)); + background-image: -webkit-linear-gradient(top, #f9f9f9, #f5f5f5); + background-image: -o-linear-gradient(top, #f9f9f9, #f5f5f5); + background-image: linear-gradient(to bottom, #f9f9f9, #f5f5f5); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#fff5f5f5', GradientType=0); + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.slider-handle { + position: absolute; + width: 20px; + height: 20px; + background-color: #0e90d2; + background-image: -moz-linear-gradient(top, #149bdf, #0480be); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); + background-image: -webkit-linear-gradient(top, #149bdf, #0480be); + background-image: -o-linear-gradient(top, #149bdf, #0480be); + background-image: linear-gradient(to bottom, #149bdf, #0480be); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); + -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + opacity: 0.8; + border: 0px solid transparent; +} +.slider-handle.round { + -webkit-border-radius: 20px; + -moz-border-radius: 20px; + border-radius: 20px; +} +.slider-handle.triangle { + background: transparent none; } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocontextmenu/editor_plugin_src.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocontextmenu/editor_plugin_src.js index afb82f1098..f3074ef21f 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocontextmenu/editor_plugin_src.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocontextmenu/editor_plugin_src.js @@ -1,69 +1,69 @@ -/** -* editor_plugin_src.js -* -* Copyright 2012, Umbraco -* Released under MIT License. -* -* License: http://opensource.org/licenses/mit-license.html -*/ - -(function () { - var Event = tinymce.dom.Event, each = tinymce.each, DOM = tinymce.DOM; - - /** - * This plugin modifies the standard TinyMCE context menu, with umbraco specific changes. - * - * @class tinymce.plugins.umbContextMenu - */ - tinymce.create('tinymce.plugins.UmbracoContextMenu', { - /** - * Initializes the plugin, this will be executed after the plugin has been created. - * This call is done before the editor instance has finished it's initialization so use the onInit event - * of the editor instance to intercept that event. - * - * @method init - * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. - * @param {string} url Absolute URL to where the plugin is located. - */ - init: function (ed) { - if (ed.plugins.contextmenu) { - - ed.plugins.contextmenu.onContextMenu.add(function (th, menu, event) { - - var keys = UmbClientMgr.uiKeys(); - - $.each(menu.items, function (idx, el) { - - switch (el.settings.cmd) { - case "Cut": - el.settings.title = keys['defaultdialogs_cut']; - break; - case "Copy": - el.settings.title = keys['general_copy']; - break; - case "Paste": - el.settings.title = keys['defaultdialogs_paste']; - break; - case "mceAdvLink": - case "mceLink": - el.settings.title = keys['defaultdialogs_insertlink']; - break; - case "UnLink": - el.settings.title = keys['relatedlinks_removeLink']; - break; - case "mceImage": - el.settings.title = keys['defaultdialogs_insertimage']; - el.settings.cmd = "mceUmbimage"; - break; - } - - }); - - }); - } - } - }); - - // Register plugin - tinymce.PluginManager.add('umbracocontextmenu', tinymce.plugins.UmbracoContextMenu); -})(); +/** +* editor_plugin_src.js +* +* Copyright 2012, Umbraco +* Released under MIT License. +* +* License: http://opensource.org/licenses/mit-license.html +*/ + +(function () { + var Event = tinymce.dom.Event, each = tinymce.each, DOM = tinymce.DOM; + + /** + * This plugin modifies the standard TinyMCE context menu, with umbraco specific changes. + * + * @class tinymce.plugins.umbContextMenu + */ + tinymce.create('tinymce.plugins.UmbracoContextMenu', { + /** + * Initializes the plugin, this will be executed after the plugin has been created. + * This call is done before the editor instance has finished it's initialization so use the onInit event + * of the editor instance to intercept that event. + * + * @method init + * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. + * @param {string} url Absolute URL to where the plugin is located. + */ + init: function (ed) { + if (ed.plugins.contextmenu) { + + ed.plugins.contextmenu.onContextMenu.add(function (th, menu, event) { + + var keys = UmbClientMgr.uiKeys(); + + $.each(menu.items, function (idx, el) { + + switch (el.settings.cmd) { + case "Cut": + el.settings.title = keys['defaultdialogs_cut']; + break; + case "Copy": + el.settings.title = keys['general_copy']; + break; + case "Paste": + el.settings.title = keys['defaultdialogs_paste']; + break; + case "mceAdvLink": + case "mceLink": + el.settings.title = keys['defaultdialogs_insertlink']; + break; + case "UnLink": + el.settings.title = keys['relatedlinks_removeLink']; + break; + case "mceImage": + el.settings.title = keys['defaultdialogs_insertimage']; + el.settings.cmd = "mceUmbimage"; + break; + } + + }); + + }); + } + } + }); + + // Register plugin + tinymce.PluginManager.add('umbracocontextmenu', tinymce.plugins.UmbracoContextMenu); +})(); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocontextmenu/plugin.min.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocontextmenu/plugin.min.js index afb82f1098..f3074ef21f 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocontextmenu/plugin.min.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocontextmenu/plugin.min.js @@ -1,69 +1,69 @@ -/** -* editor_plugin_src.js -* -* Copyright 2012, Umbraco -* Released under MIT License. -* -* License: http://opensource.org/licenses/mit-license.html -*/ - -(function () { - var Event = tinymce.dom.Event, each = tinymce.each, DOM = tinymce.DOM; - - /** - * This plugin modifies the standard TinyMCE context menu, with umbraco specific changes. - * - * @class tinymce.plugins.umbContextMenu - */ - tinymce.create('tinymce.plugins.UmbracoContextMenu', { - /** - * Initializes the plugin, this will be executed after the plugin has been created. - * This call is done before the editor instance has finished it's initialization so use the onInit event - * of the editor instance to intercept that event. - * - * @method init - * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. - * @param {string} url Absolute URL to where the plugin is located. - */ - init: function (ed) { - if (ed.plugins.contextmenu) { - - ed.plugins.contextmenu.onContextMenu.add(function (th, menu, event) { - - var keys = UmbClientMgr.uiKeys(); - - $.each(menu.items, function (idx, el) { - - switch (el.settings.cmd) { - case "Cut": - el.settings.title = keys['defaultdialogs_cut']; - break; - case "Copy": - el.settings.title = keys['general_copy']; - break; - case "Paste": - el.settings.title = keys['defaultdialogs_paste']; - break; - case "mceAdvLink": - case "mceLink": - el.settings.title = keys['defaultdialogs_insertlink']; - break; - case "UnLink": - el.settings.title = keys['relatedlinks_removeLink']; - break; - case "mceImage": - el.settings.title = keys['defaultdialogs_insertimage']; - el.settings.cmd = "mceUmbimage"; - break; - } - - }); - - }); - } - } - }); - - // Register plugin - tinymce.PluginManager.add('umbracocontextmenu', tinymce.plugins.UmbracoContextMenu); -})(); +/** +* editor_plugin_src.js +* +* Copyright 2012, Umbraco +* Released under MIT License. +* +* License: http://opensource.org/licenses/mit-license.html +*/ + +(function () { + var Event = tinymce.dom.Event, each = tinymce.each, DOM = tinymce.DOM; + + /** + * This plugin modifies the standard TinyMCE context menu, with umbraco specific changes. + * + * @class tinymce.plugins.umbContextMenu + */ + tinymce.create('tinymce.plugins.UmbracoContextMenu', { + /** + * Initializes the plugin, this will be executed after the plugin has been created. + * This call is done before the editor instance has finished it's initialization so use the onInit event + * of the editor instance to intercept that event. + * + * @method init + * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. + * @param {string} url Absolute URL to where the plugin is located. + */ + init: function (ed) { + if (ed.plugins.contextmenu) { + + ed.plugins.contextmenu.onContextMenu.add(function (th, menu, event) { + + var keys = UmbClientMgr.uiKeys(); + + $.each(menu.items, function (idx, el) { + + switch (el.settings.cmd) { + case "Cut": + el.settings.title = keys['defaultdialogs_cut']; + break; + case "Copy": + el.settings.title = keys['general_copy']; + break; + case "Paste": + el.settings.title = keys['defaultdialogs_paste']; + break; + case "mceAdvLink": + case "mceLink": + el.settings.title = keys['defaultdialogs_insertlink']; + break; + case "UnLink": + el.settings.title = keys['relatedlinks_removeLink']; + break; + case "mceImage": + el.settings.title = keys['defaultdialogs_insertimage']; + el.settings.cmd = "mceUmbimage"; + break; + } + + }); + + }); + } + } + }); + + // Register plugin + tinymce.PluginManager.add('umbracocontextmenu', tinymce.plugins.UmbracoContextMenu); +})(); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/js/dialog.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/js/dialog.js index a7ee507e06..fa8341132f 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/js/dialog.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/js/dialog.js @@ -1,19 +1,19 @@ -tinyMCEPopup.requireLangPack(); - -var ExampleDialog = { - init : function() { - var f = document.forms[0]; - - // Get the selected contents as text and place it in the input - f.someval.value = tinyMCEPopup.editor.selection.getContent({format : 'text'}); - f.somearg.value = tinyMCEPopup.getWindowArg('some_custom_arg'); - }, - - insert : function() { - // Insert the contents from the input into the document - tinyMCEPopup.editor.execCommand('mceInsertContent', false, document.forms[0].someval.value); - tinyMCEPopup.close(); - } -}; - -tinyMCEPopup.onInit.add(ExampleDialog.init, ExampleDialog); +tinyMCEPopup.requireLangPack(); + +var ExampleDialog = { + init : function() { + var f = document.forms[0]; + + // Get the selected contents as text and place it in the input + f.someval.value = tinyMCEPopup.editor.selection.getContent({format : 'text'}); + f.somearg.value = tinyMCEPopup.getWindowArg('some_custom_arg'); + }, + + insert : function() { + // Insert the contents from the input into the document + tinyMCEPopup.editor.execCommand('mceInsertContent', false, document.forms[0].someval.value); + tinyMCEPopup.close(); + } +}; + +tinyMCEPopup.onInit.add(ExampleDialog.init, ExampleDialog); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/en.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/en.js index f3721d3a31..e0784f80f4 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/en.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/en.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('en.example',{ - desc : 'This is just a template button' -}); +tinyMCE.addI18n('en.example',{ + desc : 'This is just a template button' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/en_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/en_dlg.js index a9cd65f8c0..ebcf948dac 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/en_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/en_dlg.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('en.example_dlg',{ - title : 'This is just a example title' -}); +tinyMCE.addI18n('en.example_dlg',{ + title : 'This is just a example title' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/en_us.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/en_us.js index 740ff625dd..fbda3698e0 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/en_us.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/en_us.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('en_us.example',{ - desc : 'This is just a template button' -}); +tinyMCE.addI18n('en_us.example',{ + desc : 'This is just a template button' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/en_us_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/en_us_dlg.js index 1128298d38..0468c4553c 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/en_us_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/en_us_dlg.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('en_us.example_dlg',{ - title : 'This is just a example title' -}); +tinyMCE.addI18n('en_us.example_dlg',{ + title : 'This is just a example title' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/it.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/it.js index 1e2d9a5088..64b457b77a 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/it.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/it.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('it.example',{ -desc : 'Esempio di pulsante' -}); +tinyMCE.addI18n('it.example',{ +desc : 'Esempio di pulsante' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/it_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/it_dlg.js index 8ee63fb598..5231d1bcb8 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/it_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/it_dlg.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('it.example_dlg',{ -title : 'Esempio di titolo' -}); +tinyMCE.addI18n('it.example_dlg',{ +title : 'Esempio di titolo' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/sv.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/sv.js index d4e15110fa..4759e3c71f 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/sv.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/sv.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('sv.example',{ - desc : 'Detta är bara en mallknapp' -}); +tinyMCE.addI18n('sv.example',{ + desc : 'Detta är bara en mallknapp' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/sv_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/sv_dlg.js index 0208b8069e..6ac1706907 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/sv_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/langs/sv_dlg.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('sv.example_dlg',{ - title : 'Detta är bara ett exempel pÃ¥ en titel' -}); +tinyMCE.addI18n('sv.example_dlg',{ + title : 'Detta är bara ett exempel pÃ¥ en titel' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/plugin.min.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/plugin.min.js index 3551a1cdef..5c69c72080 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/plugin.min.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracocss/plugin.min.js @@ -1,182 +1,182 @@ -/** -* $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $ -* -* @author Moxiecode -* @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved. -*/ - -(function () { - // Load plugin specific language pack - // tinymce.PluginManager.requireLangPack('umbraco'); - - tinymce.create('tinymce.plugins.umbracocss', { - /** - * Initializes the plugin, this will be executed after the plugin has been created. - * This call is done before the editor instance has finished it's initialization so use the onInit event - * of the editor instance to intercept that event. - * - * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. - * @param {string} url Absolute URL to where the plugin is located. - */ - init: function (ed, url) { - - this.editor = ed; - - // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceExample'); - ed.addCommand('mceumbracosetstyle', function () { - alert('blah'); - }); - - - // Add a node change handler, selects the button in the UI when a image is selected - ed.onNodeChange.add(function (ed, cm, n) { - var c = cm.get('umbracostyles'); - var formatSelected = false; - - if (c) { - // check for element - var el = tinymce.DOM.getParent(n, ed.dom.isBlock); - if (el) { - for (var i = 0; i < c.items.length; i++) { - if (c.items[i].value == el.nodeName.toLowerCase()) { - c.select(el.nodeName.toLowerCase()); - formatSelected = true; - } - } - } - - // check for class - if (n.className != '') { - if (c) { - c.select('.' + n.className); - } - } else if (c && !formatSelected) { - c.select(); // reset selector if no class or block elements - } - } - - /* if (c = cm.get('styleselect')) { - if (n.className) { - t._importClasses(); - c.select(n.className); - } else - c.select(); - } - - if (c = cm.get('formatselect')) { - p = DOM.getParent(n, DOM.isBlock); - - if (p) - c.select(p.nodeName.toLowerCase()); - } - */ - }); - }, - - /** - * Creates control instances based in the incomming name. This method is normally not - * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons - * but you sometimes need to create more complex controls like listboxes, split buttons etc then this - * method can be used to create those. - * - * @param {String} n Name of the control to create. - * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control. - * @return {tinymce.ui.Control} New control instance or null if no control was created. - */ - createControl: function (n, cm) { - - // add style dropdown - if (n == 'umbracocss') { - - var umbracoStyles = this.editor.getParam('theme_umbraco_styles').split(';'); - - var styles = cm.createListBox('umbracostyles', { - title: this.editor.getLang('umbraco.style_select'), - onselect: function (v) { - if (v == '') { - if (styles.selectedValue.indexOf('.') == 0) { - // remove style - var selectedStyle = styles.selectedValue; - var styleObj = tinymce.activeEditor.formatter.get('umb' + selectedStyle.substring(1, selectedStyle.length)); - if (styleObj == undefined) { - tinymce.activeEditor.formatter.register('umb' + selectedStyle.substring(1, selectedStyle.length), { - inline: 'span', - selector: '*', - classes: selectedStyle.substring(1, selectedStyle.length) - }); - } - tinyMCE.activeEditor.formatter.remove('umb' + selectedStyle.substring(1, selectedStyle.length)); - - // tinymce.activeEditor.execCommand('mceSetStyleInfo', 0, { command: 'removeformat' }); - } else { - // remove block element - tinymce.activeEditor.execCommand('FormatBlock', false, 'p'); - } - } - else if (v.indexOf('.') != '0') { - tinymce.activeEditor.execCommand('FormatBlock', false, v); - } else { - // use new formatting engine - if (tinymce.activeEditor.formatter.get('umb' + v.substring(1, v.length)) == undefined) { - tinymce.activeEditor.formatter.register('umb' + v.substring(1, v.length), { - inline: 'span', - selector: '*', - classes: v.substring(1, v.length) - }); - } - var styleObj = tinymce.activeEditor.formatter.get('umb' + v.substring(1, v.length)); - tinyMCE.activeEditor.formatter.apply('umb' + v.substring(1, v.length)); - - // tinyMCE.activeEditor.execCommand('mceSetCSSClass', false, v.substring(1, v.length)); - - } - return false; - } - }); - - // add styles - for (var i = 0; i < umbracoStyles.length; i++) { - if (umbracoStyles[i] != '') { - var name = umbracoStyles[i].substring(0, umbracoStyles[i].indexOf("=")); - var alias = umbracoStyles[i].substring(umbracoStyles[i].indexOf("=") + 1, umbracoStyles[i].length); - - if (alias.indexOf('.') < 0) - alias = alias.toLowerCase(); - else if (alias.length > 1) { - // register with new formatter engine (can't access from here so a hack in the set style above!) - // tinyMCE.activeEditor.formatter.register('umb' + alias.substring(1, alias.length), { - // classes: alias.substring(1, alias.length) - // }); - } - styles.add(name, alias); - } - } - - - return styles; - } - - return null; - }, - - - /** - * Returns information about the plugin as a name/value array. - * The current keys are longname, author, authorurl, infourl and version. - * - * @return {Object} Name/value array containing information about the plugin. - */ - getInfo: function () { - return { - longname: 'Umbraco CSS/Styling Plugin', - author: 'Umbraco', - authorurl: 'http://umbraco.org', - infourl: 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/example', - version: "1.0" - }; - } - }); - - // Register plugin - tinymce.PluginManager.add('umbracocss', tinymce.plugins.umbracocss); +/** +* $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $ +* +* @author Moxiecode +* @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved. +*/ + +(function () { + // Load plugin specific language pack + // tinymce.PluginManager.requireLangPack('umbraco'); + + tinymce.create('tinymce.plugins.umbracocss', { + /** + * Initializes the plugin, this will be executed after the plugin has been created. + * This call is done before the editor instance has finished it's initialization so use the onInit event + * of the editor instance to intercept that event. + * + * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. + * @param {string} url Absolute URL to where the plugin is located. + */ + init: function (ed, url) { + + this.editor = ed; + + // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceExample'); + ed.addCommand('mceumbracosetstyle', function () { + alert('blah'); + }); + + + // Add a node change handler, selects the button in the UI when a image is selected + ed.onNodeChange.add(function (ed, cm, n) { + var c = cm.get('umbracostyles'); + var formatSelected = false; + + if (c) { + // check for element + var el = tinymce.DOM.getParent(n, ed.dom.isBlock); + if (el) { + for (var i = 0; i < c.items.length; i++) { + if (c.items[i].value == el.nodeName.toLowerCase()) { + c.select(el.nodeName.toLowerCase()); + formatSelected = true; + } + } + } + + // check for class + if (n.className != '') { + if (c) { + c.select('.' + n.className); + } + } else if (c && !formatSelected) { + c.select(); // reset selector if no class or block elements + } + } + + /* if (c = cm.get('styleselect')) { + if (n.className) { + t._importClasses(); + c.select(n.className); + } else + c.select(); + } + + if (c = cm.get('formatselect')) { + p = DOM.getParent(n, DOM.isBlock); + + if (p) + c.select(p.nodeName.toLowerCase()); + } + */ + }); + }, + + /** + * Creates control instances based in the incomming name. This method is normally not + * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons + * but you sometimes need to create more complex controls like listboxes, split buttons etc then this + * method can be used to create those. + * + * @param {String} n Name of the control to create. + * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control. + * @return {tinymce.ui.Control} New control instance or null if no control was created. + */ + createControl: function (n, cm) { + + // add style dropdown + if (n == 'umbracocss') { + + var umbracoStyles = this.editor.getParam('theme_umbraco_styles').split(';'); + + var styles = cm.createListBox('umbracostyles', { + title: this.editor.getLang('umbraco.style_select'), + onselect: function (v) { + if (v == '') { + if (styles.selectedValue.indexOf('.') == 0) { + // remove style + var selectedStyle = styles.selectedValue; + var styleObj = tinymce.activeEditor.formatter.get('umb' + selectedStyle.substring(1, selectedStyle.length)); + if (styleObj == undefined) { + tinymce.activeEditor.formatter.register('umb' + selectedStyle.substring(1, selectedStyle.length), { + inline: 'span', + selector: '*', + classes: selectedStyle.substring(1, selectedStyle.length) + }); + } + tinyMCE.activeEditor.formatter.remove('umb' + selectedStyle.substring(1, selectedStyle.length)); + + // tinymce.activeEditor.execCommand('mceSetStyleInfo', 0, { command: 'removeformat' }); + } else { + // remove block element + tinymce.activeEditor.execCommand('FormatBlock', false, 'p'); + } + } + else if (v.indexOf('.') != '0') { + tinymce.activeEditor.execCommand('FormatBlock', false, v); + } else { + // use new formatting engine + if (tinymce.activeEditor.formatter.get('umb' + v.substring(1, v.length)) == undefined) { + tinymce.activeEditor.formatter.register('umb' + v.substring(1, v.length), { + inline: 'span', + selector: '*', + classes: v.substring(1, v.length) + }); + } + var styleObj = tinymce.activeEditor.formatter.get('umb' + v.substring(1, v.length)); + tinyMCE.activeEditor.formatter.apply('umb' + v.substring(1, v.length)); + + // tinyMCE.activeEditor.execCommand('mceSetCSSClass', false, v.substring(1, v.length)); + + } + return false; + } + }); + + // add styles + for (var i = 0; i < umbracoStyles.length; i++) { + if (umbracoStyles[i] != '') { + var name = umbracoStyles[i].substring(0, umbracoStyles[i].indexOf("=")); + var alias = umbracoStyles[i].substring(umbracoStyles[i].indexOf("=") + 1, umbracoStyles[i].length); + + if (alias.indexOf('.') < 0) + alias = alias.toLowerCase(); + else if (alias.length > 1) { + // register with new formatter engine (can't access from here so a hack in the set style above!) + // tinyMCE.activeEditor.formatter.register('umb' + alias.substring(1, alias.length), { + // classes: alias.substring(1, alias.length) + // }); + } + styles.add(name, alias); + } + } + + + return styles; + } + + return null; + }, + + + /** + * Returns information about the plugin as a name/value array. + * The current keys are longname, author, authorurl, infourl and version. + * + * @return {Object} Name/value array containing information about the plugin. + */ + getInfo: function () { + return { + longname: 'Umbraco CSS/Styling Plugin', + author: 'Umbraco', + authorurl: 'http://umbraco.org', + infourl: 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/example', + version: "1.0" + }; + } + }); + + // Register plugin + tinymce.PluginManager.add('umbracocss', tinymce.plugins.umbracocss); })(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/dialog.htm b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/dialog.htm index 6aa8bb314d..a89fcc1283 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/dialog.htm +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/dialog.htm @@ -1,92 +1,92 @@ - - - - {#embed_dlg.title} - - - - - - - - -
    - -
    -
    -
    - {#embed_dlg.general} - - - - - - - - - -
    - -
    - - - - - - -
    x   
    -
    - -
    -
    - {#embed_dlg.preview} -
    -
    - -
    -
    -
    -
    -
    -
    - {#embed_dlg.source} - -
    -
    -
    - - -
    - - -
    -
    - - - + + + + {#embed_dlg.title} + + + + + + + + +
    + +
    +
    +
    + {#embed_dlg.general} + + + + + + + + + +
    + +
    + + + + + + +
    x   
    +
    + +
    +
    + {#embed_dlg.preview} +
    +
    + +
    +
    +
    +
    +
    +
    + {#embed_dlg.source} + +
    +
    +
    + + +
    + + +
    +
    + + + diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/editor_plugin_src.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/editor_plugin_src.js index 11e31cf7e4..4649f37ecf 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/editor_plugin_src.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/editor_plugin_src.js @@ -1,84 +1,84 @@ -/** - * editor_plugin_src.js - * - * Copyright 2009, Moxiecode Systems AB - * Released under LGPL License. - * - * License: http://tinymce.moxiecode.com/license - * Contributing: http://tinymce.moxiecode.com/contributing - */ - -(function() { - // Load plugin specific language pack - tinymce.PluginManager.requireLangPack('umbracoembed'); - - tinymce.create('tinymce.plugins.umbracoembed', { - /** - * Initializes the plugin, this will be executed after the plugin has been created. - * This call is done before the editor instance has finished it's initialization so use the onInit event - * of the editor instance to intercept that event. - * - * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. - * @param {string} url Absolute URL to where the plugin is located. - */ - init : function(ed, url) { - // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceExample'); - ed.addCommand('mceUmbracoEmbed', function() { - ed.windowManager.open({ - file : url + '/dialog.htm', - width : 600 + parseInt(ed.getLang('example.delta_width', 0)), - height : 400 + parseInt(ed.getLang('example.delta_height', 0)), - inline : 1 - }, { - plugin_url : url, // Plugin absolute URL - some_custom_arg : 'custom arg' // Custom argument - }); - }); - - // Register example button - ed.addButton('umbracoembed', { - title : 'umbracoembed.desc', - cmd : 'mceUmbracoEmbed', - image : url + '/img/embed.gif' - }); - - // Add a node change handler, selects the button in the UI when a image is selected - /*ed.onNodeChange.add(function(ed, cm, n) { - cm.setActive('example', n.nodeName == 'IMG'); - });*/ - }, - - /** - * Creates control instances based in the incomming name. This method is normally not - * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons - * but you sometimes need to create more complex controls like listboxes, split buttons etc then this - * method can be used to create those. - * - * @param {String} n Name of the control to create. - * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control. - * @return {tinymce.ui.Control} New control instance or null if no control was created. - */ - createControl : function(n, cm) { - return null; - }, - - /** - * Returns information about the plugin as a name/value array. - * The current keys are longname, author, authorurl, infourl and version. - * - * @return {Object} Name/value array containing information about the plugin. - */ - getInfo : function() { - return { - longname : 'Umbraco Embed', - author : 'Tim Geyssens', - authorurl : 'http://http://umbraco.com/', - infourl : 'http://http://umbraco.com/', - version : "1.0" - }; - } - }); - - // Register plugin - tinymce.PluginManager.add('umbracoembed', tinymce.plugins.umbracoembed); +/** + * editor_plugin_src.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +(function() { + // Load plugin specific language pack + tinymce.PluginManager.requireLangPack('umbracoembed'); + + tinymce.create('tinymce.plugins.umbracoembed', { + /** + * Initializes the plugin, this will be executed after the plugin has been created. + * This call is done before the editor instance has finished it's initialization so use the onInit event + * of the editor instance to intercept that event. + * + * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. + * @param {string} url Absolute URL to where the plugin is located. + */ + init : function(ed, url) { + // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceExample'); + ed.addCommand('mceUmbracoEmbed', function() { + ed.windowManager.open({ + file : url + '/dialog.htm', + width : 600 + parseInt(ed.getLang('example.delta_width', 0)), + height : 400 + parseInt(ed.getLang('example.delta_height', 0)), + inline : 1 + }, { + plugin_url : url, // Plugin absolute URL + some_custom_arg : 'custom arg' // Custom argument + }); + }); + + // Register example button + ed.addButton('umbracoembed', { + title : 'umbracoembed.desc', + cmd : 'mceUmbracoEmbed', + image : url + '/img/embed.gif' + }); + + // Add a node change handler, selects the button in the UI when a image is selected + /*ed.onNodeChange.add(function(ed, cm, n) { + cm.setActive('example', n.nodeName == 'IMG'); + });*/ + }, + + /** + * Creates control instances based in the incomming name. This method is normally not + * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons + * but you sometimes need to create more complex controls like listboxes, split buttons etc then this + * method can be used to create those. + * + * @param {String} n Name of the control to create. + * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control. + * @return {tinymce.ui.Control} New control instance or null if no control was created. + */ + createControl : function(n, cm) { + return null; + }, + + /** + * Returns information about the plugin as a name/value array. + * The current keys are longname, author, authorurl, infourl and version. + * + * @return {Object} Name/value array containing information about the plugin. + */ + getInfo : function() { + return { + longname : 'Umbraco Embed', + author : 'Tim Geyssens', + authorurl : 'http://http://umbraco.com/', + infourl : 'http://http://umbraco.com/', + version : "1.0" + }; + } + }); + + // Register plugin + tinymce.PluginManager.add('umbracoembed', tinymce.plugins.umbracoembed); })(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/js/dialog.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/js/dialog.js index 9c7870cc33..4cf224eb04 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/js/dialog.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/js/dialog.js @@ -1,90 +1,90 @@ -tinyMCEPopup.requireLangPack(); - -var UmbracoEmbedDialog = { - insert: function () { - // Insert the contents from the input into the document - tinyMCEPopup.editor.execCommand('mceInsertContent', false, $('#source').val()); - tinyMCEPopup.close(); - }, - showPreview: function () { - $('#insert').attr('disabled', 'disabled'); - - var url = $('#url').val(); - var width = $('#width').val(); ; - var height = $('#height').val(); ; - - $('#preview').html('loading'); - $('#source').val(''); - - $.ajax({ - type: 'POST', - async: true, - url: '../../../../base/EmbedMediaService/Embed/', - data: { url: url, width: width, height: height }, - dataType: 'json', - success: function (result) { - switch (result.Status) { - case 0: - //not supported - $('#preview').html('Not Supported'); - break; - case 1: - //error - $('#preview').html('Error'); - break; - case 2: - $('#preview').html(result.Markup); - $('#source').val(result.Markup); - if (result.SupportsDimensions) { - $('#dimensions').show(); - } else { - $('#dimensions').hide(); - } - $('#insert').removeAttr('disabled'); - break; - } - }, - error: function (xhr, ajaxOptions, thrownError) { - $('#preview').html("Error"); - } - }); - }, - beforeResize: function () { - this.width = parseInt($('#width').val(), 10); - this.height = parseInt($('#height').val(), 10); - }, - changeSize: function (type) { - var width, height, scale, size; - - if ($('#constrain').is(':checked')) { - width = parseInt($('#width').val(), 10); - height = parseInt($('#height').val(), 10); - if (type == 'width') { - this.height = Math.round((width / this.width) * height); - $('#height').val(this.height); - } else { - this.width = Math.round((height / this.height) * width); - $('#width').val(this.width); - } - } - if ($('#url').val() != '') { - UmbracoEmbedDialog.showPreview(); - } - }, - changeSource: function (type) { - if ($('#source').val() != '') { - $('#insert').removeAttr('disabled'); - } - else { - $('#insert').attr('disabled', 'disabled'); - } - }, - updatePreviewFromSource: function (type) { - var sourceVal = $('#source').val(); - - if (sourceVal != '') { - $('#preview').html(sourceVal); - } - } -}; - +tinyMCEPopup.requireLangPack(); + +var UmbracoEmbedDialog = { + insert: function () { + // Insert the contents from the input into the document + tinyMCEPopup.editor.execCommand('mceInsertContent', false, $('#source').val()); + tinyMCEPopup.close(); + }, + showPreview: function () { + $('#insert').attr('disabled', 'disabled'); + + var url = $('#url').val(); + var width = $('#width').val(); ; + var height = $('#height').val(); ; + + $('#preview').html('loading'); + $('#source').val(''); + + $.ajax({ + type: 'POST', + async: true, + url: '../../../../base/EmbedMediaService/Embed/', + data: { url: url, width: width, height: height }, + dataType: 'json', + success: function (result) { + switch (result.Status) { + case 0: + //not supported + $('#preview').html('Not Supported'); + break; + case 1: + //error + $('#preview').html('Error'); + break; + case 2: + $('#preview').html(result.Markup); + $('#source').val(result.Markup); + if (result.SupportsDimensions) { + $('#dimensions').show(); + } else { + $('#dimensions').hide(); + } + $('#insert').removeAttr('disabled'); + break; + } + }, + error: function (xhr, ajaxOptions, thrownError) { + $('#preview').html("Error"); + } + }); + }, + beforeResize: function () { + this.width = parseInt($('#width').val(), 10); + this.height = parseInt($('#height').val(), 10); + }, + changeSize: function (type) { + var width, height, scale, size; + + if ($('#constrain').is(':checked')) { + width = parseInt($('#width').val(), 10); + height = parseInt($('#height').val(), 10); + if (type == 'width') { + this.height = Math.round((width / this.width) * height); + $('#height').val(this.height); + } else { + this.width = Math.round((height / this.height) * width); + $('#width').val(this.width); + } + } + if ($('#url').val() != '') { + UmbracoEmbedDialog.showPreview(); + } + }, + changeSource: function (type) { + if ($('#source').val() != '') { + $('#insert').removeAttr('disabled'); + } + else { + $('#insert').attr('disabled', 'disabled'); + } + }, + updatePreviewFromSource: function (type) { + var sourceVal = $('#source').val(); + + if (sourceVal != '') { + $('#preview').html(sourceVal); + } + } +}; + diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/da.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/da.js index 6108245c2c..a93d2e36f2 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/da.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/da.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('da.umbracoembed', { - desc: 'Inds\u00E6t ekstern mediefil' -}); +tinyMCE.addI18n('da.umbracoembed', { + desc: 'Inds\u00E6t ekstern mediefil' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/da_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/da_dlg.js index b550ed1956..3082589539 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/da_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/da_dlg.js @@ -1,9 +1,9 @@ -tinyMCE.addI18n('da.embed_dlg', { - title: 'Inds\u00E6t ekstern mediefil', - general: 'Generelt', - url: 'Url:', - size: 'Dimensioner:', - constrain_proportions: 'Bevar proportioner', - preview: 'Vis', - source: 'Vis kilde' -}); +tinyMCE.addI18n('da.embed_dlg', { + title: 'Inds\u00E6t ekstern mediefil', + general: 'Generelt', + url: 'Url:', + size: 'Dimensioner:', + constrain_proportions: 'Bevar proportioner', + preview: 'Vis', + source: 'Vis kilde' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/de.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/de.js index e0429d74b0..ad0b940580 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/de.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/de.js @@ -1,10 +1,10 @@ -tinyMCE.addI18n('de.embed_dlg', { - title: 'Medien von Drittanbietern einbetten', - general: 'Allgemein', - url: 'Url:', - size: 'Abmessungen:', - constrain_proportions: 'Proportionen beibehalten', - preview: 'Vorschau', - source: 'Quellcode' - -}); +tinyMCE.addI18n('de.embed_dlg', { + title: 'Medien von Drittanbietern einbetten', + general: 'Allgemein', + url: 'Url:', + size: 'Abmessungen:', + constrain_proportions: 'Proportionen beibehalten', + preview: 'Vorschau', + source: 'Quellcode' + +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/de_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/de_dlg.js index e0429d74b0..ad0b940580 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/de_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/de_dlg.js @@ -1,10 +1,10 @@ -tinyMCE.addI18n('de.embed_dlg', { - title: 'Medien von Drittanbietern einbetten', - general: 'Allgemein', - url: 'Url:', - size: 'Abmessungen:', - constrain_proportions: 'Proportionen beibehalten', - preview: 'Vorschau', - source: 'Quellcode' - -}); +tinyMCE.addI18n('de.embed_dlg', { + title: 'Medien von Drittanbietern einbetten', + general: 'Allgemein', + url: 'Url:', + size: 'Abmessungen:', + constrain_proportions: 'Proportionen beibehalten', + preview: 'Vorschau', + source: 'Quellcode' + +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/en.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/en.js index 9b00025854..2b086df34e 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/en.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/en.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('en.umbracoembed', { - desc: 'Embed third party media' +tinyMCE.addI18n('en.umbracoembed', { + desc: 'Embed third party media' }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/en_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/en_dlg.js index c66fe9bec8..e131d87533 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/en_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/en_dlg.js @@ -1,10 +1,10 @@ -tinyMCE.addI18n('en.embed_dlg', { - title: 'Embed third party media', - general: 'General', - url: 'Url:', - size: 'Size:', - constrain_proportions: 'Constrain', - preview: 'Preview', - source: 'Source' - -}); +tinyMCE.addI18n('en.embed_dlg', { + title: 'Embed third party media', + general: 'General', + url: 'Url:', + size: 'Size:', + constrain_proportions: 'Constrain', + preview: 'Preview', + source: 'Source' + +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/en_us.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/en_us.js index a9435b319b..4698979aab 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/en_us.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/en_us.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('en_us.umbracoembed', { - desc: 'Embed third party media' +tinyMCE.addI18n('en_us.umbracoembed', { + desc: 'Embed third party media' }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/en_us_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/en_us_dlg.js index 9501a9c675..8c6a070226 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/en_us_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/en_us_dlg.js @@ -1,10 +1,10 @@ -tinyMCE.addI18n('en_us.embed_dlg', { - title: 'Embed third party media', - general: 'General', - url: 'Url:', - size: 'Size:', - constrain_proportions: 'Constrain', - preview: 'Preview', - source: 'Source' - -}); +tinyMCE.addI18n('en_us.embed_dlg', { + title: 'Embed third party media', + general: 'General', + url: 'Url:', + size: 'Size:', + constrain_proportions: 'Constrain', + preview: 'Preview', + source: 'Source' + +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/it.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/it.js index a34f018b1a..a8ff6693ab 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/it.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/it.js @@ -1,9 +1,9 @@ -tinyMCE.addI18n('en.embed_dlg', { -title: 'Integra media di terze parti', -general: 'Generale', -url: 'Url:', -size: 'Dimensione:', -constrain_proportions: 'Vincolo', -preview: 'Anteprima', -source: 'Sorgente' -}); +tinyMCE.addI18n('en.embed_dlg', { +title: 'Integra media di terze parti', +general: 'Generale', +url: 'Url:', +size: 'Dimensione:', +constrain_proportions: 'Vincolo', +preview: 'Anteprima', +source: 'Sorgente' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/it_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/it_dlg.js index 8209a7c0df..87766ce2da 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/it_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/it_dlg.js @@ -1,9 +1,9 @@ -tinyMCE.addI18n('it.embed_dlg', { -title: 'Integra media di terze parti', -general: 'Generale', -url: 'Url:', -size: 'Dimensione:', -constrain_proportions: 'Vincolo', -preview: 'Anteprima', -source: 'Sorgente' -}); +tinyMCE.addI18n('it.embed_dlg', { +title: 'Integra media di terze parti', +general: 'Generale', +url: 'Url:', +size: 'Dimensione:', +constrain_proportions: 'Vincolo', +preview: 'Anteprima', +source: 'Sorgente' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/sv.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/sv.js index 85238dce96..36bcaf8758 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/sv.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/sv.js @@ -1,10 +1,10 @@ -tinyMCE.addI18n('sv.embed_dlg', { - title: 'Bädda in tredjeparts media', - general: 'Generell', - url: 'Url:', - size: 'Storlek:', - constrain_proportions: 'BibehÃ¥ll proportioner', - preview: 'Förhandsgranska', - source: 'Källa' - +tinyMCE.addI18n('sv.embed_dlg', { + title: 'Bädda in tredjeparts media', + general: 'Generell', + url: 'Url:', + size: 'Storlek:', + constrain_proportions: 'BibehÃ¥ll proportioner', + preview: 'Förhandsgranska', + source: 'Källa' + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/sv_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/sv_dlg.js index 85238dce96..36bcaf8758 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/sv_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoembed/langs/sv_dlg.js @@ -1,10 +1,10 @@ -tinyMCE.addI18n('sv.embed_dlg', { - title: 'Bädda in tredjeparts media', - general: 'Generell', - url: 'Url:', - size: 'Storlek:', - constrain_proportions: 'BibehÃ¥ll proportioner', - preview: 'Förhandsgranska', - source: 'Källa' - +tinyMCE.addI18n('sv.embed_dlg', { + title: 'Bädda in tredjeparts media', + general: 'Generell', + url: 'Url:', + size: 'Storlek:', + constrain_proportions: 'BibehÃ¥ll proportioner', + preview: 'Förhandsgranska', + source: 'Källa' + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/editor_plugin_src.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/editor_plugin_src.js index f8a344c5e1..accb078909 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/editor_plugin_src.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/editor_plugin_src.js @@ -1,52 +1,52 @@ -/** - * $Id: editor_plugin_src.js 677 2008-03-07 13:52:41Z spocke $ - * - * @author Moxiecode - * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved. - */ - -(function() { -// tinymce.PluginManager.requireLangPack('umbraco'); - - tinymce.create('tinymce.plugins.UmbracoImagePlugin', { - init: function(ed, url) { - // Register commands - ed.addCommand('mceUmbimage', function() { - // Internal image object like a flash placeholder - if (ed.dom.getAttrib(ed.selection.getNode(), 'class').indexOf('mceItem') != -1) - return; - - ed.windowManager.open({ - /* UMBRACO SPECIFIC: Load Umbraco modal window */ - file: tinyMCE.activeEditor.getParam('umbraco_path') + '/plugins/tinymce3/insertImage.aspx', - width: 575 + ed.getLang('umbracoimg.delta_width', 0), - height: 505 + ed.getLang('umbracoimg.delta_height', 0), - inline: 1 - }, { - plugin_url: url - }); - }); - - // Register buttons - ed.addButton('image', { - title: 'advimage.image_desc', - cmd: 'mceUmbimage' - }); - - }, - - getInfo: function() { - return { - longname: 'Umbraco image dialog', - author: 'Umbraco', - authorurl: 'http://umbraco.org', - infourl: 'http://umbraco.org', - version: "1.0" - }; - } - }); - - // Register plugin - tinymce.PluginManager.add('umbracoimg', tinymce.plugins.UmbracoImagePlugin); - +/** + * $Id: editor_plugin_src.js 677 2008-03-07 13:52:41Z spocke $ + * + * @author Moxiecode + * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved. + */ + +(function() { +// tinymce.PluginManager.requireLangPack('umbraco'); + + tinymce.create('tinymce.plugins.UmbracoImagePlugin', { + init: function(ed, url) { + // Register commands + ed.addCommand('mceUmbimage', function() { + // Internal image object like a flash placeholder + if (ed.dom.getAttrib(ed.selection.getNode(), 'class').indexOf('mceItem') != -1) + return; + + ed.windowManager.open({ + /* UMBRACO SPECIFIC: Load Umbraco modal window */ + file: tinyMCE.activeEditor.getParam('umbraco_path') + '/plugins/tinymce3/insertImage.aspx', + width: 575 + ed.getLang('umbracoimg.delta_width', 0), + height: 505 + ed.getLang('umbracoimg.delta_height', 0), + inline: 1 + }, { + plugin_url: url + }); + }); + + // Register buttons + ed.addButton('image', { + title: 'advimage.image_desc', + cmd: 'mceUmbimage' + }); + + }, + + getInfo: function() { + return { + longname: 'Umbraco image dialog', + author: 'Umbraco', + authorurl: 'http://umbraco.org', + infourl: 'http://umbraco.org', + version: "1.0" + }; + } + }); + + // Register plugin + tinymce.PluginManager.add('umbracoimg', tinymce.plugins.UmbracoImagePlugin); + })(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/js/image.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/js/image.js index 870e9fbb39..25d0028fe8 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/js/image.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/js/image.js @@ -1,332 +1,332 @@ -var ImageDialog = { - preInit: function() { - var url; - - tinyMCEPopup.requireLangPack(); - - if (url = tinyMCEPopup.getParam("external_image_list_url")) - document.write(''); - }, - - init: function(ed) { - var f = document.forms[0], nl = f.elements, ed = tinyMCEPopup.editor, dom = ed.dom, n = ed.selection.getNode(); - - tinyMCEPopup.resizeToInnerSize(); - - if (n.nodeName == 'IMG') { - nl.src.value = dom.getAttrib(n, 'src'); - nl.width.value = dom.getAttrib(n, 'width'); - nl.height.value = dom.getAttrib(n, 'height'); - nl.alt.value = dom.getAttrib(n, 'alt'); - nl.orgHeight.value = dom.getAttrib(n, 'rel').split(",")[1]; - nl.orgWidth.value = dom.getAttrib(n, 'rel').split(",")[0]; - - } - - // If option enabled default contrain proportions to checked - if ((ed.getParam("advimage_constrain_proportions", true)) && f.constrain) - f.constrain.checked = true; - - this.changeAppearance(); - this.showPreviewImage(nl.src.value, 1); - }, - - insert: function(file, title) { - var ed = tinyMCEPopup.editor, t = this, f = document.forms[0]; - - if (f.src.value === '') { - if (ed.selection.getNode().nodeName == 'IMG') { - ed.dom.remove(ed.selection.getNode()); - ed.execCommand('mceRepaint'); - } - - tinyMCEPopup.close(); - return; - } - - if (tinyMCEPopup.getParam("accessibility_warnings", 1)) { - if (!f.alt.value) { - tinyMCEPopup.confirm(tinyMCEPopup.getLang('advimage_dlg.missing_alt'), function(s) { - if (s) - t.insertAndClose(); - }); - - return; - } - } - - t.insertAndClose(); - }, - - insertAndClose: function() { - var ed = tinyMCEPopup.editor, f = document.forms[0], nl = f.elements, v, args = {}, el; - - tinyMCEPopup.restoreSelection(); - - // Fixes crash in Safari - if (tinymce.isWebKit) - ed.getWin().focus(); - - if (!ed.settings.inline_styles) { - args = { - vspace: nl.vspace.value, - hspace: nl.hspace.value, - border: nl.border.value, - align: getSelectValue(f, 'align') - }; - } else { - // Remove deprecated values - args = { - vspace: '', - hspace: '', - border: '', - align: '' - }; - } - - tinymce.extend(args, { - src: nl.src.value, - width: nl.width.value, - height: nl.height.value, - alt: nl.alt.value, - title: nl.alt.value, - rel: nl.orgWidth.value + ',' + nl.orgHeight.value - }); - - args.onmouseover = args.onmouseout = ''; - - el = ed.selection.getNode(); - - if (el && el.nodeName == 'IMG') { - ed.dom.setAttribs(el, args); - } else { - ed.execCommand('mceInsertContent', false, '', { skip_undo: 1 }); - ed.dom.setAttribs('__mce_tmp', args); - ed.dom.setAttrib('__mce_tmp', 'id', ''); - ed.undoManager.add(); - } - - tinyMCEPopup.close(); - }, - - getAttrib: function(e, at) { - var ed = tinyMCEPopup.editor, dom = ed.dom, v, v2; - - if (ed.settings.inline_styles) { - switch (at) { - case 'align': - if (v = dom.getStyle(e, 'float')) - return v; - - if (v = dom.getStyle(e, 'vertical-align')) - return v; - - break; - - case 'hspace': - v = dom.getStyle(e, 'margin-left') - v2 = dom.getStyle(e, 'margin-right'); - - if (v && v == v2) - return parseInt(v.replace(/[^0-9]/g, '')); - - break; - - case 'vspace': - v = dom.getStyle(e, 'margin-top') - v2 = dom.getStyle(e, 'margin-bottom'); - if (v && v == v2) - return parseInt(v.replace(/[^0-9]/g, '')); - - break; - - case 'border': - v = 0; - - tinymce.each(['top', 'right', 'bottom', 'left'], function(sv) { - sv = dom.getStyle(e, 'border-' + sv + '-width'); - - // False or not the same as prev - if (!sv || (sv != v && v !== 0)) { - v = 0; - return false; - } - - if (sv) - v = sv; - }); - - if (v) - return parseInt(v.replace(/[^0-9]/g, '')); - - break; - } - } - - if (v = dom.getAttrib(e, at)) - return v; - - return ''; - }, - - setSwapImage: function(st) { - var f = document.forms[0]; - - f.onmousemovecheck.checked = st; - setBrowserDisabled('overbrowser', !st); - setBrowserDisabled('outbrowser', !st); - - if (f.over_list) - f.over_list.disabled = !st; - - if (f.out_list) - f.out_list.disabled = !st; - - f.onmouseoversrc.disabled = !st; - f.onmouseoutsrc.disabled = !st; - }, - - resetImageData: function() { - var f = document.forms[0]; - - f.elements.width.value = f.elements.height.value = ''; - }, - - updateImageData: function(img, st) { - var f = document.forms[0]; - - if (!st) { - f.elements.width.value = img.width; - f.elements.height.value = img.height; - } - - this.preloadImg = img; - }, - - changeAppearance: function() { - var ed = tinyMCEPopup.editor, f = document.forms[0], img = document.getElementById('alignSampleImg'); - - if (img) { - if (ed.getParam('inline_styles')) { - ed.dom.setAttrib(img, 'style', f.style.value); - } else { - img.align = f.align.value; - img.border = f.border.value; - img.hspace = f.hspace.value; - img.vspace = f.vspace.value; - } - } - }, - - changeHeight: function() { - var f = document.forms[0], tp, t = this; - alert(t.preloadImg); - - if (!f.constrain.checked || !t.preloadImg) { - return; - } - - if (f.width.value == '' || f.height.value == '') - return; - - tp = (parseInt(f.width.value) / parseInt(t.preloadImg.width)) * t.preloadImg.height; - f.height.value = tp.toFixed(0); - }, - - changeWidth: function() { - var f = document.forms[0], tp, t = this; - - if (!f.constrain.checked || !t.preloadImg) { - return; - } - - if (f.width.value == '' || f.height.value == '') - return; - - tp = (parseInt(f.height.value) / parseInt(t.preloadImg.height)) * t.preloadImg.width; - f.width.value = tp.toFixed(0); - }, - - updateStyle: function(ty) { - var dom = tinyMCEPopup.dom, st, v, f = document.forms[0], img = dom.create('img', { style: dom.get('style').value }); - - if (tinyMCEPopup.editor.settings.inline_styles) { - // Handle align - if (ty == 'align') { - dom.setStyle(img, 'float', ''); - dom.setStyle(img, 'vertical-align', ''); - - v = getSelectValue(f, 'align'); - if (v) { - if (v == 'left' || v == 'right') - dom.setStyle(img, 'float', v); - else - img.style.verticalAlign = v; - } - } - - // Handle border - if (ty == 'border') { - dom.setStyle(img, 'border', ''); - - v = f.border.value; - if (v || v == '0') { - if (v == '0') - img.style.border = '0'; - else - img.style.border = v + 'px solid black'; - } - } - - // Handle hspace - if (ty == 'hspace') { - dom.setStyle(img, 'marginLeft', ''); - dom.setStyle(img, 'marginRight', ''); - - v = f.hspace.value; - if (v) { - img.style.marginLeft = v + 'px'; - img.style.marginRight = v + 'px'; - } - } - - // Handle vspace - if (ty == 'vspace') { - dom.setStyle(img, 'marginTop', ''); - dom.setStyle(img, 'marginBottom', ''); - - v = f.vspace.value; - if (v) { - img.style.marginTop = v + 'px'; - img.style.marginBottom = v + 'px'; - } - } - - // Merge - dom.get('style').value = dom.serializeStyle(dom.parseStyle(img.style.cssText)); - } - }, - - changeMouseMove: function() { - }, - - showPreviewImage: function(u, st) { - if (!u) { - tinyMCEPopup.dom.setHTML('prev', ''); - return; - } - - if (!st && tinyMCEPopup.getParam("advimage_update_dimensions_onchange", true)) - this.resetImageData(); - - u = tinyMCEPopup.editor.documentBaseURI.toAbsolute(u); - - if (!st) - tinyMCEPopup.dom.setHTML('prev', ''); - else - tinyMCEPopup.dom.setHTML('prev', ''); - } -}; - -ImageDialog.preInit(); -tinyMCEPopup.onInit.add(ImageDialog.init, ImageDialog); +var ImageDialog = { + preInit: function() { + var url; + + tinyMCEPopup.requireLangPack(); + + if (url = tinyMCEPopup.getParam("external_image_list_url")) + document.write(''); + }, + + init: function(ed) { + var f = document.forms[0], nl = f.elements, ed = tinyMCEPopup.editor, dom = ed.dom, n = ed.selection.getNode(); + + tinyMCEPopup.resizeToInnerSize(); + + if (n.nodeName == 'IMG') { + nl.src.value = dom.getAttrib(n, 'src'); + nl.width.value = dom.getAttrib(n, 'width'); + nl.height.value = dom.getAttrib(n, 'height'); + nl.alt.value = dom.getAttrib(n, 'alt'); + nl.orgHeight.value = dom.getAttrib(n, 'rel').split(",")[1]; + nl.orgWidth.value = dom.getAttrib(n, 'rel').split(",")[0]; + + } + + // If option enabled default contrain proportions to checked + if ((ed.getParam("advimage_constrain_proportions", true)) && f.constrain) + f.constrain.checked = true; + + this.changeAppearance(); + this.showPreviewImage(nl.src.value, 1); + }, + + insert: function(file, title) { + var ed = tinyMCEPopup.editor, t = this, f = document.forms[0]; + + if (f.src.value === '') { + if (ed.selection.getNode().nodeName == 'IMG') { + ed.dom.remove(ed.selection.getNode()); + ed.execCommand('mceRepaint'); + } + + tinyMCEPopup.close(); + return; + } + + if (tinyMCEPopup.getParam("accessibility_warnings", 1)) { + if (!f.alt.value) { + tinyMCEPopup.confirm(tinyMCEPopup.getLang('advimage_dlg.missing_alt'), function(s) { + if (s) + t.insertAndClose(); + }); + + return; + } + } + + t.insertAndClose(); + }, + + insertAndClose: function() { + var ed = tinyMCEPopup.editor, f = document.forms[0], nl = f.elements, v, args = {}, el; + + tinyMCEPopup.restoreSelection(); + + // Fixes crash in Safari + if (tinymce.isWebKit) + ed.getWin().focus(); + + if (!ed.settings.inline_styles) { + args = { + vspace: nl.vspace.value, + hspace: nl.hspace.value, + border: nl.border.value, + align: getSelectValue(f, 'align') + }; + } else { + // Remove deprecated values + args = { + vspace: '', + hspace: '', + border: '', + align: '' + }; + } + + tinymce.extend(args, { + src: nl.src.value, + width: nl.width.value, + height: nl.height.value, + alt: nl.alt.value, + title: nl.alt.value, + rel: nl.orgWidth.value + ',' + nl.orgHeight.value + }); + + args.onmouseover = args.onmouseout = ''; + + el = ed.selection.getNode(); + + if (el && el.nodeName == 'IMG') { + ed.dom.setAttribs(el, args); + } else { + ed.execCommand('mceInsertContent', false, '', { skip_undo: 1 }); + ed.dom.setAttribs('__mce_tmp', args); + ed.dom.setAttrib('__mce_tmp', 'id', ''); + ed.undoManager.add(); + } + + tinyMCEPopup.close(); + }, + + getAttrib: function(e, at) { + var ed = tinyMCEPopup.editor, dom = ed.dom, v, v2; + + if (ed.settings.inline_styles) { + switch (at) { + case 'align': + if (v = dom.getStyle(e, 'float')) + return v; + + if (v = dom.getStyle(e, 'vertical-align')) + return v; + + break; + + case 'hspace': + v = dom.getStyle(e, 'margin-left') + v2 = dom.getStyle(e, 'margin-right'); + + if (v && v == v2) + return parseInt(v.replace(/[^0-9]/g, '')); + + break; + + case 'vspace': + v = dom.getStyle(e, 'margin-top') + v2 = dom.getStyle(e, 'margin-bottom'); + if (v && v == v2) + return parseInt(v.replace(/[^0-9]/g, '')); + + break; + + case 'border': + v = 0; + + tinymce.each(['top', 'right', 'bottom', 'left'], function(sv) { + sv = dom.getStyle(e, 'border-' + sv + '-width'); + + // False or not the same as prev + if (!sv || (sv != v && v !== 0)) { + v = 0; + return false; + } + + if (sv) + v = sv; + }); + + if (v) + return parseInt(v.replace(/[^0-9]/g, '')); + + break; + } + } + + if (v = dom.getAttrib(e, at)) + return v; + + return ''; + }, + + setSwapImage: function(st) { + var f = document.forms[0]; + + f.onmousemovecheck.checked = st; + setBrowserDisabled('overbrowser', !st); + setBrowserDisabled('outbrowser', !st); + + if (f.over_list) + f.over_list.disabled = !st; + + if (f.out_list) + f.out_list.disabled = !st; + + f.onmouseoversrc.disabled = !st; + f.onmouseoutsrc.disabled = !st; + }, + + resetImageData: function() { + var f = document.forms[0]; + + f.elements.width.value = f.elements.height.value = ''; + }, + + updateImageData: function(img, st) { + var f = document.forms[0]; + + if (!st) { + f.elements.width.value = img.width; + f.elements.height.value = img.height; + } + + this.preloadImg = img; + }, + + changeAppearance: function() { + var ed = tinyMCEPopup.editor, f = document.forms[0], img = document.getElementById('alignSampleImg'); + + if (img) { + if (ed.getParam('inline_styles')) { + ed.dom.setAttrib(img, 'style', f.style.value); + } else { + img.align = f.align.value; + img.border = f.border.value; + img.hspace = f.hspace.value; + img.vspace = f.vspace.value; + } + } + }, + + changeHeight: function() { + var f = document.forms[0], tp, t = this; + alert(t.preloadImg); + + if (!f.constrain.checked || !t.preloadImg) { + return; + } + + if (f.width.value == '' || f.height.value == '') + return; + + tp = (parseInt(f.width.value) / parseInt(t.preloadImg.width)) * t.preloadImg.height; + f.height.value = tp.toFixed(0); + }, + + changeWidth: function() { + var f = document.forms[0], tp, t = this; + + if (!f.constrain.checked || !t.preloadImg) { + return; + } + + if (f.width.value == '' || f.height.value == '') + return; + + tp = (parseInt(f.height.value) / parseInt(t.preloadImg.height)) * t.preloadImg.width; + f.width.value = tp.toFixed(0); + }, + + updateStyle: function(ty) { + var dom = tinyMCEPopup.dom, st, v, f = document.forms[0], img = dom.create('img', { style: dom.get('style').value }); + + if (tinyMCEPopup.editor.settings.inline_styles) { + // Handle align + if (ty == 'align') { + dom.setStyle(img, 'float', ''); + dom.setStyle(img, 'vertical-align', ''); + + v = getSelectValue(f, 'align'); + if (v) { + if (v == 'left' || v == 'right') + dom.setStyle(img, 'float', v); + else + img.style.verticalAlign = v; + } + } + + // Handle border + if (ty == 'border') { + dom.setStyle(img, 'border', ''); + + v = f.border.value; + if (v || v == '0') { + if (v == '0') + img.style.border = '0'; + else + img.style.border = v + 'px solid black'; + } + } + + // Handle hspace + if (ty == 'hspace') { + dom.setStyle(img, 'marginLeft', ''); + dom.setStyle(img, 'marginRight', ''); + + v = f.hspace.value; + if (v) { + img.style.marginLeft = v + 'px'; + img.style.marginRight = v + 'px'; + } + } + + // Handle vspace + if (ty == 'vspace') { + dom.setStyle(img, 'marginTop', ''); + dom.setStyle(img, 'marginBottom', ''); + + v = f.vspace.value; + if (v) { + img.style.marginTop = v + 'px'; + img.style.marginBottom = v + 'px'; + } + } + + // Merge + dom.get('style').value = dom.serializeStyle(dom.parseStyle(img.style.cssText)); + } + }, + + changeMouseMove: function() { + }, + + showPreviewImage: function(u, st) { + if (!u) { + tinyMCEPopup.dom.setHTML('prev', ''); + return; + } + + if (!st && tinyMCEPopup.getParam("advimage_update_dimensions_onchange", true)) + this.resetImageData(); + + u = tinyMCEPopup.editor.documentBaseURI.toAbsolute(u); + + if (!st) + tinyMCEPopup.dom.setHTML('prev', ''); + else + tinyMCEPopup.dom.setHTML('prev', ''); + } +}; + +ImageDialog.preInit(); +tinyMCEPopup.onInit.add(ImageDialog.init, ImageDialog); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/en_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/en_dlg.js index 0b39edbb46..36c09935a4 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/en_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/en_dlg.js @@ -1,43 +1,43 @@ -tinyMCE.addI18n('en.umbimage_dlg', { - tab_general: 'General', - tab_appearance: 'Appearance', - tab_advanced: 'Advanced', - general: 'General', - title: 'Title', - preview: 'Preview', - constrain_proportions: 'Constrain proportions', - langdir: 'Language direction', - langcode: 'Language code', - long_desc: 'Long description link', - style: 'Style', - classes: 'Classes', - ltr: 'Left to right', - rtl: 'Right to left', - id: 'Id', - map: 'Image map', - swap_image: 'Swap image', - alt_image: 'Alternative image', - mouseover: 'for mouse over', - mouseout: 'for mouse out', - misc: 'Miscellaneous', - example_img: 'Appearance preview image', - missing_alt: 'Are you sure you want to continue without including an Image Description? Without it the image may not be accessible to some users with disabilities, or to those using a text browser, or browsing the Web with images turned off.', - dialog_title: 'Insert/edit image', - src: 'Image URL', - alt: 'Image description', - list: 'Image list', - border: 'Border', - dimensions: 'Dimensions', - vspace: 'Vertical space', - hspace: 'Horizontal space', - align: 'Alignment', - align_baseline: 'Baseline', - align_top: 'Top', - align_middle: 'Middle', - align_bottom: 'Bottom', - align_texttop: 'Text top', - align_textbottom: 'Text bottom', - align_left: 'Left', - align_right: 'Right', - image_list: 'Image list' +tinyMCE.addI18n('en.umbimage_dlg', { + tab_general: 'General', + tab_appearance: 'Appearance', + tab_advanced: 'Advanced', + general: 'General', + title: 'Title', + preview: 'Preview', + constrain_proportions: 'Constrain proportions', + langdir: 'Language direction', + langcode: 'Language code', + long_desc: 'Long description link', + style: 'Style', + classes: 'Classes', + ltr: 'Left to right', + rtl: 'Right to left', + id: 'Id', + map: 'Image map', + swap_image: 'Swap image', + alt_image: 'Alternative image', + mouseover: 'for mouse over', + mouseout: 'for mouse out', + misc: 'Miscellaneous', + example_img: 'Appearance preview image', + missing_alt: 'Are you sure you want to continue without including an Image Description? Without it the image may not be accessible to some users with disabilities, or to those using a text browser, or browsing the Web with images turned off.', + dialog_title: 'Insert/edit image', + src: 'Image URL', + alt: 'Image description', + list: 'Image list', + border: 'Border', + dimensions: 'Dimensions', + vspace: 'Vertical space', + hspace: 'Horizontal space', + align: 'Alignment', + align_baseline: 'Baseline', + align_top: 'Top', + align_middle: 'Middle', + align_bottom: 'Bottom', + align_texttop: 'Text top', + align_textbottom: 'Text bottom', + align_left: 'Left', + align_right: 'Right', + image_list: 'Image list' }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/en_us_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/en_us_dlg.js index b93a7987bc..db5be8ae0b 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/en_us_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/en_us_dlg.js @@ -1,43 +1,43 @@ -tinyMCE.addI18n('en_us.umbimage_dlg', { - tab_general: 'General', - tab_appearance: 'Appearance', - tab_advanced: 'Advanced', - general: 'General', - title: 'Title', - preview: 'Preview', - constrain_proportions: 'Constrain proportions', - langdir: 'Language direction', - langcode: 'Language code', - long_desc: 'Long description link', - style: 'Style', - classes: 'Classes', - ltr: 'Left to right', - rtl: 'Right to left', - id: 'Id', - map: 'Image map', - swap_image: 'Swap image', - alt_image: 'Alternative image', - mouseover: 'for mouse over', - mouseout: 'for mouse out', - misc: 'Miscellaneous', - example_img: 'Appearance preview image', - missing_alt: 'Are you sure you want to continue without including an Image Description? Without it the image may not be accessible to some users with disabilities, or to those using a text browser, or browsing the Web with images turned off.', - dialog_title: 'Insert/edit image', - src: 'Image URL', - alt: 'Image description', - list: 'Image list', - border: 'Border', - dimensions: 'Dimensions', - vspace: 'Vertical space', - hspace: 'Horizontal space', - align: 'Alignment', - align_baseline: 'Baseline', - align_top: 'Top', - align_middle: 'Middle', - align_bottom: 'Bottom', - align_texttop: 'Text top', - align_textbottom: 'Text bottom', - align_left: 'Left', - align_right: 'Right', - image_list: 'Image list' +tinyMCE.addI18n('en_us.umbimage_dlg', { + tab_general: 'General', + tab_appearance: 'Appearance', + tab_advanced: 'Advanced', + general: 'General', + title: 'Title', + preview: 'Preview', + constrain_proportions: 'Constrain proportions', + langdir: 'Language direction', + langcode: 'Language code', + long_desc: 'Long description link', + style: 'Style', + classes: 'Classes', + ltr: 'Left to right', + rtl: 'Right to left', + id: 'Id', + map: 'Image map', + swap_image: 'Swap image', + alt_image: 'Alternative image', + mouseover: 'for mouse over', + mouseout: 'for mouse out', + misc: 'Miscellaneous', + example_img: 'Appearance preview image', + missing_alt: 'Are you sure you want to continue without including an Image Description? Without it the image may not be accessible to some users with disabilities, or to those using a text browser, or browsing the Web with images turned off.', + dialog_title: 'Insert/edit image', + src: 'Image URL', + alt: 'Image description', + list: 'Image list', + border: 'Border', + dimensions: 'Dimensions', + vspace: 'Vertical space', + hspace: 'Horizontal space', + align: 'Alignment', + align_baseline: 'Baseline', + align_top: 'Top', + align_middle: 'Middle', + align_bottom: 'Bottom', + align_texttop: 'Text top', + align_textbottom: 'Text bottom', + align_left: 'Left', + align_right: 'Right', + image_list: 'Image list' }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/he_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/he_dlg.js index e68b686dd7..98091a1b41 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/he_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/he_dlg.js @@ -1,43 +1,43 @@ -tinyMCE.addI18n('he.umbimage_dlg', { - tab_general: 'General', - tab_appearance: 'Appearance', - tab_advanced: 'Advanced', - general: 'General', - title: 'Title', - preview: 'Preview', - constrain_proportions: 'Constrain proportions', - langdir: 'Language direction', - langcode: 'Language code', - long_desc: 'Long description link', - style: 'Style', - classes: 'Classes', - ltr: 'Left to right', - rtl: 'Right to left', - id: 'Id', - map: 'Image map', - swap_image: 'Swap image', - alt_image: 'Alternative image', - mouseover: 'for mouse over', - mouseout: 'for mouse out', - misc: 'Miscellaneous', - example_img: 'Appearance preview image', - missing_alt: 'Are you sure you want to continue without including an Image Description? Without it the image may not be accessible to some users with disabilities, or to those using a text browser, or browsing the Web with images turned off.', - dialog_title: 'Insert/edit image', - src: 'Image URL', - alt: 'Image description', - list: 'Image list', - border: 'Border', - dimensions: 'Dimensions', - vspace: 'Vertical space', - hspace: 'Horizontal space', - align: 'Alignment', - align_baseline: 'Baseline', - align_top: 'Top', - align_middle: 'Middle', - align_bottom: 'Bottom', - align_texttop: 'Text top', - align_textbottom: 'Text bottom', - align_left: 'Left', - align_right: 'Right', - image_list: 'Image list' +tinyMCE.addI18n('he.umbimage_dlg', { + tab_general: 'General', + tab_appearance: 'Appearance', + tab_advanced: 'Advanced', + general: 'General', + title: 'Title', + preview: 'Preview', + constrain_proportions: 'Constrain proportions', + langdir: 'Language direction', + langcode: 'Language code', + long_desc: 'Long description link', + style: 'Style', + classes: 'Classes', + ltr: 'Left to right', + rtl: 'Right to left', + id: 'Id', + map: 'Image map', + swap_image: 'Swap image', + alt_image: 'Alternative image', + mouseover: 'for mouse over', + mouseout: 'for mouse out', + misc: 'Miscellaneous', + example_img: 'Appearance preview image', + missing_alt: 'Are you sure you want to continue without including an Image Description? Without it the image may not be accessible to some users with disabilities, or to those using a text browser, or browsing the Web with images turned off.', + dialog_title: 'Insert/edit image', + src: 'Image URL', + alt: 'Image description', + list: 'Image list', + border: 'Border', + dimensions: 'Dimensions', + vspace: 'Vertical space', + hspace: 'Horizontal space', + align: 'Alignment', + align_baseline: 'Baseline', + align_top: 'Top', + align_middle: 'Middle', + align_bottom: 'Bottom', + align_texttop: 'Text top', + align_textbottom: 'Text bottom', + align_left: 'Left', + align_right: 'Right', + image_list: 'Image list' }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/it_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/it_dlg.js index 199810d781..d1b32b26c2 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/it_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/it_dlg.js @@ -1,43 +1,43 @@ -tinyMCE.addI18n('it.umbimage_dlg', { - tab_general: 'Generale', - tab_appearance: 'Aspetto', - tab_advanced: 'Avanzate', - general: 'Generale', - title: 'Titolo', - preview: 'Anteprima', - constrain_proportions: 'Vincola proporzioni', - langdir: 'Direzione lingua', - langcode: 'Codice lingua', - long_desc: 'Descrizione lunga del collegamento', - style: 'Stile', - classes: 'Classi', - ltr: 'Da sinistra a destra', - rtl: 'Da destra a sinistra', - id: 'Id', - map: 'Image map', - swap_image: 'Swap immagine', - alt_image: 'Testo alternativo', - mouseover: 'Mouse over', - mouseout: 'Mouse out', - misc: 'Varie', - example_img: 'Aspetto anteprima immagine', - missing_alt: 'Sei sicuro di voler continuare senza includere una Descrizione dell'immagine? Se non lo fai l'immagine potrebbe risultare non accessibile per gli utenti con disabilit\u00E0, o per chi utilizza un browser di testo, o per chi naviga senza immagini.', - dialog_title: 'Inserisci/Modifica immagine', - src: 'URL immagine', - alt: 'Descrizione immagine', - list: 'Immagine lista', - border: 'Bordo', - dimensions: 'Dimensioni', - vspace: 'Spaziatura verticale', - hspace: 'Spaziatura orizzontale', - align: 'Allineamento', - align_baseline: 'Baseline', - align_top: 'Top', - align_middle: 'Middle', - align_bottom: 'Bottom', - align_texttop: 'Testo superiore', - align_textbottom: 'Testo inferiore', - align_left: 'Sinistra', - align_right: 'Destra', - image_list: 'Immagine lista' -}); +tinyMCE.addI18n('it.umbimage_dlg', { + tab_general: 'Generale', + tab_appearance: 'Aspetto', + tab_advanced: 'Avanzate', + general: 'Generale', + title: 'Titolo', + preview: 'Anteprima', + constrain_proportions: 'Vincola proporzioni', + langdir: 'Direzione lingua', + langcode: 'Codice lingua', + long_desc: 'Descrizione lunga del collegamento', + style: 'Stile', + classes: 'Classi', + ltr: 'Da sinistra a destra', + rtl: 'Da destra a sinistra', + id: 'Id', + map: 'Image map', + swap_image: 'Swap immagine', + alt_image: 'Testo alternativo', + mouseover: 'Mouse over', + mouseout: 'Mouse out', + misc: 'Varie', + example_img: 'Aspetto anteprima immagine', + missing_alt: 'Sei sicuro di voler continuare senza includere una Descrizione dell'immagine? Se non lo fai l'immagine potrebbe risultare non accessibile per gli utenti con disabilit\u00E0, o per chi utilizza un browser di testo, o per chi naviga senza immagini.', + dialog_title: 'Inserisci/Modifica immagine', + src: 'URL immagine', + alt: 'Descrizione immagine', + list: 'Immagine lista', + border: 'Bordo', + dimensions: 'Dimensioni', + vspace: 'Spaziatura verticale', + hspace: 'Spaziatura orizzontale', + align: 'Allineamento', + align_baseline: 'Baseline', + align_top: 'Top', + align_middle: 'Middle', + align_bottom: 'Bottom', + align_texttop: 'Testo superiore', + align_textbottom: 'Testo inferiore', + align_left: 'Sinistra', + align_right: 'Destra', + image_list: 'Immagine lista' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/sv_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/sv_dlg.js index 673af5212c..2c18b280c5 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/sv_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoimg/langs/sv_dlg.js @@ -1,43 +1,43 @@ -tinyMCE.addI18n('sv.umbimage_dlg', { - tab_general: 'Generellt', - tab_appearance: 'Utseende', - tab_advanced: 'Avancerat', - general: 'Generellt', - title: 'Titel', - preview: 'Förhandsgranska', - constrain_proportions: 'BibehÃ¥ll proportioner', - langdir: 'SprÃ¥kdirektion', - langcode: 'SprÃ¥kkod', - long_desc: 'LÃ¥ng länkbeskrivning', - style: 'Stil', - classes: 'Klasser', - ltr: 'Vänster till höger', - rtl: 'höger till vänster', - id: 'Id', - map: 'Bildkarta', - swap_image: 'Byt bild', - alt_image: 'Alternativ bild', - mouseover: 'För musen över', - mouseout: 'för musen utanför', - misc: 'Blandat', - example_img: 'Visning av bildförhandsgranskning', - missing_alt: 'Are you sure you want to continue without including an Image Description? Without it the image may not be accessible to some users with disabilities, or to those using a text browser, or browsing the Web with images turned off.', - dialog_title: 'Infoga/redigera bild', - src: 'Bild URL', - alt: 'Bildbeskrivning', - list: 'Bildlista', - border: 'Ram', - dimensions: 'Dimensioner', - vspace: 'Vertikalt avstÃ¥nd', - hspace: 'Horisontellt avstÃ¥nd', - align: 'Position', - align_baseline: 'Baslinje', - align_top: 'Toppen', - align_middle: 'Mitten', - align_bottom: 'Botten', - align_texttop: 'Text topp', - align_textbottom: 'Text botten', - align_left: 'Vänster', - align_right: 'Höger', - image_list: 'Bildlista' +tinyMCE.addI18n('sv.umbimage_dlg', { + tab_general: 'Generellt', + tab_appearance: 'Utseende', + tab_advanced: 'Avancerat', + general: 'Generellt', + title: 'Titel', + preview: 'Förhandsgranska', + constrain_proportions: 'BibehÃ¥ll proportioner', + langdir: 'SprÃ¥kdirektion', + langcode: 'SprÃ¥kkod', + long_desc: 'LÃ¥ng länkbeskrivning', + style: 'Stil', + classes: 'Klasser', + ltr: 'Vänster till höger', + rtl: 'höger till vänster', + id: 'Id', + map: 'Bildkarta', + swap_image: 'Byt bild', + alt_image: 'Alternativ bild', + mouseover: 'För musen över', + mouseout: 'för musen utanför', + misc: 'Blandat', + example_img: 'Visning av bildförhandsgranskning', + missing_alt: 'Are you sure you want to continue without including an Image Description? Without it the image may not be accessible to some users with disabilities, or to those using a text browser, or browsing the Web with images turned off.', + dialog_title: 'Infoga/redigera bild', + src: 'Bild URL', + alt: 'Bildbeskrivning', + list: 'Bildlista', + border: 'Ram', + dimensions: 'Dimensioner', + vspace: 'Vertikalt avstÃ¥nd', + hspace: 'Horisontellt avstÃ¥nd', + align: 'Position', + align_baseline: 'Baslinje', + align_top: 'Toppen', + align_middle: 'Mitten', + align_bottom: 'Botten', + align_texttop: 'Text topp', + align_textbottom: 'Text botten', + align_left: 'Vänster', + align_right: 'Höger', + image_list: 'Bildlista' }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/dialog.htm b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/dialog.htm index 58edf9a503..b4c62840ea 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/dialog.htm +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/dialog.htm @@ -1,27 +1,27 @@ - - - - {#example_dlg.title} - - - - - -
    -

    Here is a example dialog.

    -

    Selected text:

    -

    Custom arg:

    - -
    -
    - -
    - -
    - -
    -
    -
    - - - + + + + {#example_dlg.title} + + + + + +
    +

    Here is a example dialog.

    +

    Selected text:

    +

    Custom arg:

    + +
    +
    + +
    + +
    + +
    +
    +
    + + + diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/editor_plugin_src.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/editor_plugin_src.js index 9790c580c0..35fc20fc14 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/editor_plugin_src.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/editor_plugin_src.js @@ -1,145 +1,145 @@ -/** -* $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $ -* -* @author Moxiecode -* @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved. -*/ - -(function() { - // Load plugin specific language pack -// tinymce.PluginManager.requireLangPack('umbraco'); - - tinymce.create('tinymce.plugins.umbracomacro', { - /** - * Initializes the plugin, this will be executed after the plugin has been created. - * This call is done before the editor instance has finished it's initialization so use the onInit event - * of the editor instance to intercept that event. - * - * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. - * @param {string} url Absolute URL to where the plugin is located. - */ - init: function(ed, url) { - var t = this; - - // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceExample'); - ed.addCommand('mceumbracomacro', function() { - var se = ed.selection; - - var urlParams = ""; - var el = se.getNode(); - - // ie selector bug - if (!ed.dom.hasClass(el, 'umbMacroHolder')) { - el = ed.dom.getParent(el, 'div.umbMacroHolder'); - } - - var attrString = ""; - if (ed.dom.hasClass(el, 'umbMacroHolder')) { - for (var i = 0; i < el.attributes.length; i++) { - attrName = el.attributes[i].nodeName.toLowerCase(); - if (attrName != "mce_serialized") { - if (el.attributes[i].nodeValue && (attrName != 'ismacro' && attrName != 'style' && attrName != 'contenteditable')) { - attrString += el.attributes[i].nodeName + '=' + escape(t._utf8_encode(el.attributes[i].nodeValue)) + '&'; //.replace(/#/g, "%23").replace(/\/g, "%3E").replace(/\"/g, "%22") + '&'; - - } - } - } - - // vi trunkerer strengen ved at fjerne et evt. overskydende amp; - if (attrString.length > 0) - attrString = attrString.substr(0, attrString.length - 1); - - urlParams = "&" + attrString; - } else { - urlParams = '&umbPageId=' + tinyMCE.activeEditor.getParam('theme_umbraco_pageId') + '&umbVersionId=' + tinyMCE.activeEditor.getParam('theme_umbraco_versionId'); - } - - ed.windowManager.open({ - file: tinyMCE.activeEditor.getParam('umbraco_path') + '/plugins/tinymce3/insertMacro.aspx?editor=trueurl' + urlParams, - width: 480 + parseInt(ed.getLang('umbracomacro.delta_width', 0)), - height: 470 + parseInt(ed.getLang('umbracomacro.delta_height', 0)), - inline: 1 - }, { - plugin_url: url // Plugin absolute URL - }); - }); - - // Register example button - ed.addButton('umbracomacro', { - title: 'umbracomacro.desc', - cmd: 'mceumbracomacro', - image: url + '/img/insMacro.gif' - }); - - // Add a node change handler, test if we're editing a macro - ed.onNodeChange.addToTop(function(ed, cm, n) { - - var macroElement = ed.dom.getParent(ed.selection.getStart(), 'div.umbMacroHolder'); - - // mark button if it's a macro - cm.setActive('umbracomacro', macroElement && ed.dom.hasClass(macroElement, 'umbMacroHolder')); - - }); - }, - - _utf8_encode: function(string) { - string = string.replace(/\r\n/g, "\n"); - var utftext = ""; - - for (var n = 0; n < string.length; n++) { - - var c = string.charCodeAt(n); - - if (c < 128) { - utftext += String.fromCharCode(c); - } - else if ((c > 127) && (c < 2048)) { - utftext += String.fromCharCode((c >> 6) | 192); - utftext += String.fromCharCode((c & 63) | 128); - } - else { - utftext += String.fromCharCode((c >> 12) | 224); - utftext += String.fromCharCode(((c >> 6) & 63) | 128); - utftext += String.fromCharCode((c & 63) | 128); - } - - } - - return utftext; - }, - - /** - * Creates control instances based in the incomming name. This method is normally not - * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons - * but you sometimes need to create more complex controls like listboxes, split buttons etc then this - * method can be used to create those. - * - * @param {String} n Name of the control to create. - * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control. - * @return {tinymce.ui.Control} New control instance or null if no control was created. - */ - createControl: function(n, cm) { - return null; - }, - - - /** - * Returns information about the plugin as a name/value array. - * The current keys are longname, author, authorurl, infourl and version. - * - * @return {Object} Name/value array containing information about the plugin. - */ - getInfo: function() { - return { - longname: 'Umbraco Macro Insertion Plugin', - author: 'Umbraco', - authorurl: 'http://umbraco.org', - infourl: 'http://umbraco.org/redir/tinymcePlugins', - version: "1.0" - }; - } - }); - - // Register plugin - tinymce.PluginManager.add('umbracomacro', tinymce.plugins.umbracomacro); +/** +* $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $ +* +* @author Moxiecode +* @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved. +*/ + +(function() { + // Load plugin specific language pack +// tinymce.PluginManager.requireLangPack('umbraco'); + + tinymce.create('tinymce.plugins.umbracomacro', { + /** + * Initializes the plugin, this will be executed after the plugin has been created. + * This call is done before the editor instance has finished it's initialization so use the onInit event + * of the editor instance to intercept that event. + * + * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. + * @param {string} url Absolute URL to where the plugin is located. + */ + init: function(ed, url) { + var t = this; + + // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceExample'); + ed.addCommand('mceumbracomacro', function() { + var se = ed.selection; + + var urlParams = ""; + var el = se.getNode(); + + // ie selector bug + if (!ed.dom.hasClass(el, 'umbMacroHolder')) { + el = ed.dom.getParent(el, 'div.umbMacroHolder'); + } + + var attrString = ""; + if (ed.dom.hasClass(el, 'umbMacroHolder')) { + for (var i = 0; i < el.attributes.length; i++) { + attrName = el.attributes[i].nodeName.toLowerCase(); + if (attrName != "mce_serialized") { + if (el.attributes[i].nodeValue && (attrName != 'ismacro' && attrName != 'style' && attrName != 'contenteditable')) { + attrString += el.attributes[i].nodeName + '=' + escape(t._utf8_encode(el.attributes[i].nodeValue)) + '&'; //.replace(/#/g, "%23").replace(/\/g, "%3E").replace(/\"/g, "%22") + '&'; + + } + } + } + + // vi trunkerer strengen ved at fjerne et evt. overskydende amp; + if (attrString.length > 0) + attrString = attrString.substr(0, attrString.length - 1); + + urlParams = "&" + attrString; + } else { + urlParams = '&umbPageId=' + tinyMCE.activeEditor.getParam('theme_umbraco_pageId') + '&umbVersionId=' + tinyMCE.activeEditor.getParam('theme_umbraco_versionId'); + } + + ed.windowManager.open({ + file: tinyMCE.activeEditor.getParam('umbraco_path') + '/plugins/tinymce3/insertMacro.aspx?editor=trueurl' + urlParams, + width: 480 + parseInt(ed.getLang('umbracomacro.delta_width', 0)), + height: 470 + parseInt(ed.getLang('umbracomacro.delta_height', 0)), + inline: 1 + }, { + plugin_url: url // Plugin absolute URL + }); + }); + + // Register example button + ed.addButton('umbracomacro', { + title: 'umbracomacro.desc', + cmd: 'mceumbracomacro', + image: url + '/img/insMacro.gif' + }); + + // Add a node change handler, test if we're editing a macro + ed.onNodeChange.addToTop(function(ed, cm, n) { + + var macroElement = ed.dom.getParent(ed.selection.getStart(), 'div.umbMacroHolder'); + + // mark button if it's a macro + cm.setActive('umbracomacro', macroElement && ed.dom.hasClass(macroElement, 'umbMacroHolder')); + + }); + }, + + _utf8_encode: function(string) { + string = string.replace(/\r\n/g, "\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + + var c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if ((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + + return utftext; + }, + + /** + * Creates control instances based in the incomming name. This method is normally not + * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons + * but you sometimes need to create more complex controls like listboxes, split buttons etc then this + * method can be used to create those. + * + * @param {String} n Name of the control to create. + * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control. + * @return {tinymce.ui.Control} New control instance or null if no control was created. + */ + createControl: function(n, cm) { + return null; + }, + + + /** + * Returns information about the plugin as a name/value array. + * The current keys are longname, author, authorurl, infourl and version. + * + * @return {Object} Name/value array containing information about the plugin. + */ + getInfo: function() { + return { + longname: 'Umbraco Macro Insertion Plugin', + author: 'Umbraco', + authorurl: 'http://umbraco.org', + infourl: 'http://umbraco.org/redir/tinymcePlugins', + version: "1.0" + }; + } + }); + + // Register plugin + tinymce.PluginManager.add('umbracomacro', tinymce.plugins.umbracomacro); })(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/js/dialog.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/js/dialog.js index a7ee507e06..fa8341132f 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/js/dialog.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/js/dialog.js @@ -1,19 +1,19 @@ -tinyMCEPopup.requireLangPack(); - -var ExampleDialog = { - init : function() { - var f = document.forms[0]; - - // Get the selected contents as text and place it in the input - f.someval.value = tinyMCEPopup.editor.selection.getContent({format : 'text'}); - f.somearg.value = tinyMCEPopup.getWindowArg('some_custom_arg'); - }, - - insert : function() { - // Insert the contents from the input into the document - tinyMCEPopup.editor.execCommand('mceInsertContent', false, document.forms[0].someval.value); - tinyMCEPopup.close(); - } -}; - -tinyMCEPopup.onInit.add(ExampleDialog.init, ExampleDialog); +tinyMCEPopup.requireLangPack(); + +var ExampleDialog = { + init : function() { + var f = document.forms[0]; + + // Get the selected contents as text and place it in the input + f.someval.value = tinyMCEPopup.editor.selection.getContent({format : 'text'}); + f.somearg.value = tinyMCEPopup.getWindowArg('some_custom_arg'); + }, + + insert : function() { + // Insert the contents from the input into the document + tinyMCEPopup.editor.execCommand('mceInsertContent', false, document.forms[0].someval.value); + tinyMCEPopup.close(); + } +}; + +tinyMCEPopup.onInit.add(ExampleDialog.init, ExampleDialog); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/en.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/en.js index e0c41561cc..60b03b55c7 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/en.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/en.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('en.umbracomacro',{ - desc : 'Insert macro' -}); +tinyMCE.addI18n('en.umbracomacro',{ + desc : 'Insert macro' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/en_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/en_dlg.js index a9cd65f8c0..ebcf948dac 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/en_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/en_dlg.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('en.example_dlg',{ - title : 'This is just a example title' -}); +tinyMCE.addI18n('en.example_dlg',{ + title : 'This is just a example title' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/en_us.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/en_us.js index 4f29a4b469..61fee28c63 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/en_us.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/en_us.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('en_us.umbracomacro',{ - desc : 'Insert macro' -}); +tinyMCE.addI18n('en_us.umbracomacro',{ + desc : 'Insert macro' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/en_us_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/en_us_dlg.js index 1128298d38..0468c4553c 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/en_us_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/en_us_dlg.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('en_us.example_dlg',{ - title : 'This is just a example title' -}); +tinyMCE.addI18n('en_us.example_dlg',{ + title : 'This is just a example title' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/he_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/he_dlg.js index 87fa7ad273..390eabc168 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/he_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/he_dlg.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('he.example_dlg',{ - title : 'This is just a example title' -}); +tinyMCE.addI18n('he.example_dlg',{ + title : 'This is just a example title' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/ru_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/ru_dlg.js index 1f48ec416e..3fa610a3ee 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/ru_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/ru_dlg.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('ru.example_dlg',{ - title : 'Ýòî ïðîñòî ïðèìåð çàãîëîâêà' -}); +tinyMCE.addI18n('ru.example_dlg',{ + title : 'Ýòî ïðîñòî ïðèìåð çàãîëîâêà' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/sv.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/sv.js index b5407f47f3..fc134d5698 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/sv.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/sv.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('sv.umbracomacro',{ - desc : 'Infoga makro' -}); +tinyMCE.addI18n('sv.umbracomacro',{ + desc : 'Infoga makro' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/sv_dlg.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/sv_dlg.js index 0ceeb89f70..3bf4ed0880 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/sv_dlg.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracomacro/langs/sv_dlg.js @@ -1,3 +1,3 @@ -tinyMCE.addI18n('sv.example_dlg',{ - title : 'Detta är bar ett exempel pÃ¥ en titel' -}); +tinyMCE.addI18n('sv.example_dlg',{ + title : 'Detta är bar ett exempel pÃ¥ en titel' +}); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracopaste/editor_plugin_src.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracopaste/editor_plugin_src.js index 93ab759805..aaf58e7c2d 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracopaste/editor_plugin_src.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracopaste/editor_plugin_src.js @@ -1,53 +1,53 @@ -/** -* editor_plugin_src.js -* -* Copyright 2012, Umbraco -* Released under MIT License. -* -* License: http://opensource.org/licenses/mit-license.html -*/ - -(function () { - var Event = tinymce.dom.Event, each = tinymce.each, DOM = tinymce.DOM; - - /** - * This plugin modifies the standard TinyMCE paste, with umbraco specific changes. - * - * @class tinymce.plugins.umbContextMenu - */ - tinymce.create('tinymce.plugins.UmbracoPaste', { - /** - * Initializes the plugin, this will be executed after the plugin has been created. - * This call is done before the editor instance has finished it's initialization so use the onInit event - * of the editor instance to intercept that event. - * - * @method init - * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. - * @param {string} url Absolute URL to where the plugin is located. - */ - init: function (ed) { - var t = this; - - ed.plugins.paste.onPreProcess.add(function (pl, o) { - - var ed = this.editor, h = o.content; - - var umbracoAllowedStyles = ed.getParam('theme_umbraco_styles'); - for (var i = 1; i < 7; i++) { - if (umbracoAllowedStyles.indexOf("h" + i) == -1) { - h = h.replace(new RegExp(']*', 'gi'), '

    ', 'gi'), '

    '); - } - } - - o.content = h; - - }); - - } - - }); - - // Register plugin - tinymce.PluginManager.add('umbracopaste', tinymce.plugins.UmbracoPaste); -})(); +/** +* editor_plugin_src.js +* +* Copyright 2012, Umbraco +* Released under MIT License. +* +* License: http://opensource.org/licenses/mit-license.html +*/ + +(function () { + var Event = tinymce.dom.Event, each = tinymce.each, DOM = tinymce.DOM; + + /** + * This plugin modifies the standard TinyMCE paste, with umbraco specific changes. + * + * @class tinymce.plugins.umbContextMenu + */ + tinymce.create('tinymce.plugins.UmbracoPaste', { + /** + * Initializes the plugin, this will be executed after the plugin has been created. + * This call is done before the editor instance has finished it's initialization so use the onInit event + * of the editor instance to intercept that event. + * + * @method init + * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. + * @param {string} url Absolute URL to where the plugin is located. + */ + init: function (ed) { + var t = this; + + ed.plugins.paste.onPreProcess.add(function (pl, o) { + + var ed = this.editor, h = o.content; + + var umbracoAllowedStyles = ed.getParam('theme_umbraco_styles'); + for (var i = 1; i < 7; i++) { + if (umbracoAllowedStyles.indexOf("h" + i) == -1) { + h = h.replace(new RegExp(']*', 'gi'), '

    ', 'gi'), '

    '); + } + } + + o.content = h; + + }); + + } + + }); + + // Register plugin + tinymce.PluginManager.add('umbracopaste', tinymce.plugins.UmbracoPaste); +})(); diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoshortcut/editor_plugin_src.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoshortcut/editor_plugin_src.js index ef5f844d1c..15d669b4e1 100644 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoshortcut/editor_plugin_src.js +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracoshortcut/editor_plugin_src.js @@ -1,43 +1,43 @@ - -(function () { - tinymce.create('tinymce.plugins.Umbracoshortcut', { - init: function (ed, url) { - var t = this; - var ctrlPressed = false; - - t.editor = ed; - - ed.onKeyDown.add(function (ed, e) { - if (e.keyCode == 17) - ctrlPressed = true; - - if (ctrlPressed && e.keyCode == 83) { - jQuery(document).trigger("UMBRACO_TINYMCE_SAVE", e); - ctrlPressed = false; - tinymce.dom.Event.cancel(e); - return false; - } - }); - - ed.onKeyUp.add(function (ed, e) { - if (e.keyCode == 17) - ctrlPressed = false; - }); - }, - - getInfo: function () { - return { - longname: 'Umbraco Save short cut key', - author: 'Umbraco HQ', - authorurl: 'http://umbraco.com', - infourl: 'http://our.umbraco.org', - version: "1.0" - }; - } - - // Private methods - }); - - // Register plugin - tinymce.PluginManager.add('umbracoshortcut', tinymce.plugins.Umbracoshortcut); + +(function () { + tinymce.create('tinymce.plugins.Umbracoshortcut', { + init: function (ed, url) { + var t = this; + var ctrlPressed = false; + + t.editor = ed; + + ed.onKeyDown.add(function (ed, e) { + if (e.keyCode == 17) + ctrlPressed = true; + + if (ctrlPressed && e.keyCode == 83) { + jQuery(document).trigger("UMBRACO_TINYMCE_SAVE", e); + ctrlPressed = false; + tinymce.dom.Event.cancel(e); + return false; + } + }); + + ed.onKeyUp.add(function (ed, e) { + if (e.keyCode == 17) + ctrlPressed = false; + }); + }, + + getInfo: function () { + return { + longname: 'Umbraco Save short cut key', + author: 'Umbraco HQ', + authorurl: 'http://umbraco.com', + infourl: 'http://our.umbraco.org', + version: "1.0" + }; + } + + // Private methods + }); + + // Register plugin + tinymce.PluginManager.add('umbracoshortcut', tinymce.plugins.Umbracoshortcut); })(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/skins/umbraco/content.min.css b/src/Umbraco.Web.UI.Client/lib/tinymce/skins/umbraco/content.min.css index 1c409a7758..cb2ed40889 100755 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/skins/umbraco/content.min.css +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/skins/umbraco/content.min.css @@ -1,95 +1,95 @@ -body.mce-content-body { - background-color: #fff; - font-family: Verdana,Arial,Helvetica,sans-serif; - font-size: 11px; - scrollbar-3dlight-color: #f0f0ee; - scrollbar-arrow-color: #676662; - scrollbar-base-color: #f0f0ee; - scrollbar-darkshadow-color: #ddd; - scrollbar-face-color: #e0e0dd; - scrollbar-highlight-color: #f0f0ee; - scrollbar-shadow-color: #f0f0ee; - scrollbar-track-color: #f5f5f5; -} - -td, th { - font-family: Verdana,Arial,Helvetica,sans-serif; - font-size: 11px; -} - -.mce-object { - border: 1px dotted #3a3a3a; - background: #d5d5d5 url(img/object.gif) no-repeat center; -} - -.mce-pagebreak { - cursor: default; - display: block; - border: 0; - width: 100%; - height: 5px; - border: 1px dashed #666; - margin-top: 15px; -} - -.mce-item-anchor { - cursor: default; - display: inline-block; - -webkit-user-select: all; - -webkit-user-modify: read-only; - -moz-user-select: all; - -moz-user-modify: read-only; - width: 9px!important; - height: 9px!important; - border: 1px dotted #3a3a3a; - background: #d5d5d5 url(img/anchor.gif) no-repeat center; -} - -.mce-nbsp { - background: #AAA; -} - -hr { - cursor: default; -} - -.mce-match-marker { - background: green; - color: #fff; -} - -.mce-spellchecker-word { - background: url(img/wline.gif) repeat-x bottom left; - cursor: default; -} - -.mce-item-table, .mce-item-table td, .mce-item-table th, .mce-item-table caption { - border: 1px dashed #BBB; -} - -td.mce-item-selected, th.mce-item-selected { - background-color: #39f!important; -} - -.mce-edit-focus { - outline: 1px dotted #333; -} - -/* TINYMCE Macro styles*/ -.mce-content-body .umb-macro-holder -{ - border: 3px dotted orange; - padding: 7px; - display:block; - margin:3px; -} - -/* loader for macro loading in tinymce*/ - .mce-content-body .umb-macro-holder.loading { - background: url(img/loader.gif) right no-repeat; - -moz-background-size: 18px; - -o-background-size: 18px; - -webkit-background-size: 18px; - background-size: 18px; - background-position-x: 99%; +body.mce-content-body { + background-color: #fff; + font-family: Verdana,Arial,Helvetica,sans-serif; + font-size: 11px; + scrollbar-3dlight-color: #f0f0ee; + scrollbar-arrow-color: #676662; + scrollbar-base-color: #f0f0ee; + scrollbar-darkshadow-color: #ddd; + scrollbar-face-color: #e0e0dd; + scrollbar-highlight-color: #f0f0ee; + scrollbar-shadow-color: #f0f0ee; + scrollbar-track-color: #f5f5f5; +} + +td, th { + font-family: Verdana,Arial,Helvetica,sans-serif; + font-size: 11px; +} + +.mce-object { + border: 1px dotted #3a3a3a; + background: #d5d5d5 url(img/object.gif) no-repeat center; +} + +.mce-pagebreak { + cursor: default; + display: block; + border: 0; + width: 100%; + height: 5px; + border: 1px dashed #666; + margin-top: 15px; +} + +.mce-item-anchor { + cursor: default; + display: inline-block; + -webkit-user-select: all; + -webkit-user-modify: read-only; + -moz-user-select: all; + -moz-user-modify: read-only; + width: 9px!important; + height: 9px!important; + border: 1px dotted #3a3a3a; + background: #d5d5d5 url(img/anchor.gif) no-repeat center; +} + +.mce-nbsp { + background: #AAA; +} + +hr { + cursor: default; +} + +.mce-match-marker { + background: green; + color: #fff; +} + +.mce-spellchecker-word { + background: url(img/wline.gif) repeat-x bottom left; + cursor: default; +} + +.mce-item-table, .mce-item-table td, .mce-item-table th, .mce-item-table caption { + border: 1px dashed #BBB; +} + +td.mce-item-selected, th.mce-item-selected { + background-color: #39f!important; +} + +.mce-edit-focus { + outline: 1px dotted #333; +} + +/* TINYMCE Macro styles*/ +.mce-content-body .umb-macro-holder +{ + border: 3px dotted orange; + padding: 7px; + display:block; + margin:3px; +} + +/* loader for macro loading in tinymce*/ + .mce-content-body .umb-macro-holder.loading { + background: url(img/loader.gif) right no-repeat; + -moz-background-size: 18px; + -o-background-size: 18px; + -webkit-background-size: 18px; + background-size: 18px; + background-position-x: 99%; } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js b/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js index ee72b70286..234fdd1078 100644 --- a/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js +++ b/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js @@ -1,357 +1,357 @@ - -//TODO: WE NEED TO CONVERT ALL OF THESE METHODS TO PROXY TO OUR APPLICATION SINCE MANY CUSTOM APPS USE THIS! - -//TEST to mock iframe, this intercepts calls directly -//to the old iframe, and funnels requests to angular directly -//var right = {document: {location: {}}}; -/**/ - -Umbraco.Sys.registerNamespace("Umbraco.Application"); - -(function($) { - Umbraco.Application.ClientManager = function() { - - //to support those trying to call right.document.etc - var fakeFrame = {}; - Object.defineProperty(fakeFrame, "href", { - get: function() { - return this._href ? this._href : ""; - }, - set: function(value) { - this._href = value; - UmbClientMgr.contentFrame(value); - }, - }); - - /** - * @ngdoc function - * @name getRootScope - * @methodOf UmbClientMgr - * @function - * - * @description - * Returns the root angular scope - */ - function getRootScope() { - return angular.element(document.getElementById("umbracoMainPageBody")).scope(); - } - - /** - * @ngdoc function - * @name getRootInjector - * @methodOf UmbClientMgr - * @function - * - * @description - * Returns the root angular injector - */ - function getRootInjector() { - return angular.element(document.getElementById("umbracoMainPageBody")).injector(); - } - - - return { - _isDirty: false, - _isDebug: false, - _mainTree: null, - _appActions: null, - _historyMgr: null, - _rootPath: "/umbraco", //this is the default - _modal: new Array(), //track all modal window objects (they get stacked) - - historyManager: function () { - - throw "Not implemented!"; - - //if (!this._historyMgr) { - // this._historyMgr = new Umbraco.Controls.HistoryManager(); - //} - //return this._historyMgr; - }, - - setUmbracoPath: function(strPath) { - /// - /// sets the Umbraco root path folder - /// - this._debug("setUmbracoPath: " + strPath); - this._rootPath = strPath; - }, - - mainWindow: function() { - return top; - }, - mainTree: function() { - var injector = getRootInjector(); - var navService = injector.get("navigationService"); - - //mimic the API of the legacy tree - var tree = { - setActiveTreeType : function(treeType){ - navService.setActiveTreeType(treeType); - }, - syncTree : function(path,forceReload){ - navService.syncPath(path, forceReload); - }, - clearTreeCache: function(){ - var treeService = injector.get("treeService"); - treeService.clearCache(); - }, - reloadActionNode: function(){ - navService.reloadNode(); - }, - refreshTree: function(treeAlias){ - navService.setActiveTreeType(treeAlias); - }, - moveNode: function (id, path) { - if (navService.ui.currentNode) { - var treeService = injector.get("treeService"); - var treeRoot = treeService.getTreeRoot(navService.ui.currentNode); - if (treeRoot) { - var found = treeService.getDescendantNode(treeRoot, id); - if (found) { - treeService.removeNode(found); - } - } - } - navService.syncPath(path, true); - }, - getActionNode: function () { - //need to replicate the legacy tree node - var legacyNode = { - nodeId: navService.ui.currentNode.id, - nodeName: navService.ui.currentNode.name, - nodeType: navService.ui.currentNode.nodeType, - treeType: navService.ui.currentNode.nodeType, - sourceUrl: navService.ui.currentNode.childNodesUrl, - updateDefinition: function() { - throw "'updateDefinition' method is not supported in Umbraco 7, consider upgrading to the new v7 APIs"; - } - }; - //defined getters that will throw a not implemented/supported exception - Object.defineProperty(legacyNode, "menu", { - get: function () { - throw "'menu' property is not supported in Umbraco 7, consider upgrading to the new v7 APIs"; - } - }); - Object.defineProperty(legacyNode, "jsNode", { - get: function () { - throw "'jsNode' property is not supported in Umbraco 7, consider upgrading to the new v7 APIs"; - } - }); - Object.defineProperty(legacyNode, "jsTree", { - get: function () { - throw "'jsTree' property is not supported in Umbraco 7, consider upgrading to the new v7 APIs"; - } - }); - - return legacyNode; - } - }; - - return tree; - }, - appActions: function() { - var injector = getRootInjector(); - var navService = injector.get("navigationService"); - - var _actions = {}; - _actions.openDashboard = function(section){ - navService.changeSection(section); - }; - - return _actions; - //throw "Not implemented!"; - - ////if the main window has no actions, we'll create some - //if (this._appActions == null) { - // if (typeof this.mainWindow().appActions == 'undefined') { - // this._appActions = new Umbraco.Application.Actions(); - // } - // else this._appActions = this.mainWindow().appActions; - //} - //return this._appActions; - }, - uiKeys: function() { - - throw "Not implemented!"; - - ////TODO: If there is no main window, we need to go retrieve the appActions from the server! - //return this.mainWindow().uiKeys; - }, - contentFrameAndSection: function(app, rightFrameUrl) { - - throw "Not implemented!"; - - ////this.appActions().shiftApp(app, this.uiKeys()['sections_' + app]); - //var self = this; - //self.mainWindow().UmbClientMgr.historyManager().addHistory(app, true); - //window.setTimeout(function() { - // self.mainWindow().UmbClientMgr.contentFrame(rightFrameUrl); - //}, 200); - }, - - /** - * @ngdoc function - * @name contentFrame - * @methodOf UmbClientMgr - * @function - * - * @description - * This will tell our angular app to create and load in an iframe at the specified location - * @param strLocation {String} The URL to load the iframe in - */ - contentFrame: function (strLocation) { - - if (!strLocation || strLocation == "") { - //SD: NOTE: We used to return the content iframe object but now I'm not sure we should do that ?! - - if (typeof top.right != "undefined") { - return top.right; - } - else { - return top; //return the current window if the content frame doesn't exist in the current context - } - //return; - } - - this._debug("contentFrame: " + strLocation); - - //get our angular navigation service - var injector = getRootInjector(); - var navService = injector.get("navigationService"); - - //if the path doesn't start with "/" or with the root path then - //prepend the root path - if (!strLocation.startsWith("/")) { - strLocation = this._rootPath + "/" + strLocation; - } - else if (strLocation.length >= this._rootPath.length - && strLocation.substr(0, this._rootPath.length) != this._rootPath) { - strLocation = this._rootPath + "/" + strLocation; - } - - navService.loadLegacyIFrame(strLocation); - - }, - - getFakeFrame : function() { - return fakeFrame; - }, - - /** This is used to launch an angular based modal window instead of the legacy window */ - openAngularModalWindow: function (options) { - - //get our angular navigation service - var injector = getRootInjector(); - var dialogService = injector.get("dialogService"); - - var dialog = dialogService.open(options); - - ////add the callback to the jquery data for the modal so we can call it on close to support the legacy way dialogs worked. - //dialog.element.data("modalCb", onCloseCallback); - - //this._modal.push(dialog); - //return dialog; - }, - - openModalWindow: function(url, name, showHeader, width, height, top, leftOffset, closeTriggers, onCloseCallback) { - //need to create the modal on the top window if the top window has a client manager, if not, create it on the current window - - //get our angular navigation service - var injector = getRootInjector(); - var navService = injector.get("navigationService"); - var dialogService = injector.get("dialogService"); - - var self = this; - - //based on what state the nav ui is in, depends on how we are going to launch a model / dialog. A modal - // will show up on the right hand side and a dialog will show up as if it is in the menu. - // with the legacy API we cannot know what is expected so we can only check if the menu is active, if it is - // we'll launch a dialog, otherwise a modal. - var dialog; - if (navService.ui.currentMode === "menu") { - dialog = navService.showDialog({ - //create a 'fake' action to passin with the specified actionUrl since it needs to load into an iframe - action: { - name: name, - metaData: { - actionUrl: url - } - }, - node: {} - }); - } - else { - dialog = dialogService.open({ - template: url, - width: width, - height: height, - iframe: true, - show: true - }); - } - - - //add the callback to the jquery data for the modal so we can call it on close to support the legacy way dialogs worked. - dialog.element.data("modalCb", onCloseCallback); - //add the close triggers - if (angular.isArray(closeTriggers)) { - for (var i = 0; i < closeTriggers.length; i++) { - var e = dialog.find(closeTriggers[i]); - if (e.length > 0) { - e.click(function () { - self.closeModalWindow(); - }); - } - } - } - - this._modal.push(dialog); - return dialog; - }, - closeModalWindow: function(rVal) { - - //get our angular navigation service - var injector = getRootInjector(); - var dialogService = injector.get("dialogService"); - - // all legacy calls to closeModalWindow are expecting to just close the last opened one so we'll ensure - // that this is still the case. - if (this._modal != null && this._modal.length > 0) { - - var lastModal = this._modal.pop(); - - //if we've stored a callback on this modal call it before we close. - var self = this; - //get the compat callback from the modal element - var onCloseCallback = lastModal.element.data("modalCb"); - if (typeof onCloseCallback == "function") { - onCloseCallback.apply(self, [{ outVal: rVal }]); - } - - //just call the native dialog close() method to remove the dialog - lastModal.close(); - } - else { - //instead of calling just the dialog service we funnel it through the global - //event emitter - getRootScope().$emit("closeDialogs", event); - } - }, - _debug: function(strMsg) { - if (this._isDebug) { - Sys.Debug.trace("UmbClientMgr: " + strMsg); - } - }, - get_isDirty: function() { - return this._isDirty; - }, - set_isDirty: function(value) { - this._isDirty = value; - } - }; - }; -})(jQuery); - -//define alias for use throughout application -var UmbClientMgr = new Umbraco.Application.ClientManager(); + +//TODO: WE NEED TO CONVERT ALL OF THESE METHODS TO PROXY TO OUR APPLICATION SINCE MANY CUSTOM APPS USE THIS! + +//TEST to mock iframe, this intercepts calls directly +//to the old iframe, and funnels requests to angular directly +//var right = {document: {location: {}}}; +/**/ + +Umbraco.Sys.registerNamespace("Umbraco.Application"); + +(function($) { + Umbraco.Application.ClientManager = function() { + + //to support those trying to call right.document.etc + var fakeFrame = {}; + Object.defineProperty(fakeFrame, "href", { + get: function() { + return this._href ? this._href : ""; + }, + set: function(value) { + this._href = value; + UmbClientMgr.contentFrame(value); + }, + }); + + /** + * @ngdoc function + * @name getRootScope + * @methodOf UmbClientMgr + * @function + * + * @description + * Returns the root angular scope + */ + function getRootScope() { + return angular.element(document.getElementById("umbracoMainPageBody")).scope(); + } + + /** + * @ngdoc function + * @name getRootInjector + * @methodOf UmbClientMgr + * @function + * + * @description + * Returns the root angular injector + */ + function getRootInjector() { + return angular.element(document.getElementById("umbracoMainPageBody")).injector(); + } + + + return { + _isDirty: false, + _isDebug: false, + _mainTree: null, + _appActions: null, + _historyMgr: null, + _rootPath: "/umbraco", //this is the default + _modal: new Array(), //track all modal window objects (they get stacked) + + historyManager: function () { + + throw "Not implemented!"; + + //if (!this._historyMgr) { + // this._historyMgr = new Umbraco.Controls.HistoryManager(); + //} + //return this._historyMgr; + }, + + setUmbracoPath: function(strPath) { + /// + /// sets the Umbraco root path folder + /// + this._debug("setUmbracoPath: " + strPath); + this._rootPath = strPath; + }, + + mainWindow: function() { + return top; + }, + mainTree: function() { + var injector = getRootInjector(); + var navService = injector.get("navigationService"); + + //mimic the API of the legacy tree + var tree = { + setActiveTreeType : function(treeType){ + navService.setActiveTreeType(treeType); + }, + syncTree : function(path,forceReload){ + navService.syncPath(path, forceReload); + }, + clearTreeCache: function(){ + var treeService = injector.get("treeService"); + treeService.clearCache(); + }, + reloadActionNode: function(){ + navService.reloadNode(); + }, + refreshTree: function(treeAlias){ + navService.setActiveTreeType(treeAlias); + }, + moveNode: function (id, path) { + if (navService.ui.currentNode) { + var treeService = injector.get("treeService"); + var treeRoot = treeService.getTreeRoot(navService.ui.currentNode); + if (treeRoot) { + var found = treeService.getDescendantNode(treeRoot, id); + if (found) { + treeService.removeNode(found); + } + } + } + navService.syncPath(path, true); + }, + getActionNode: function () { + //need to replicate the legacy tree node + var legacyNode = { + nodeId: navService.ui.currentNode.id, + nodeName: navService.ui.currentNode.name, + nodeType: navService.ui.currentNode.nodeType, + treeType: navService.ui.currentNode.nodeType, + sourceUrl: navService.ui.currentNode.childNodesUrl, + updateDefinition: function() { + throw "'updateDefinition' method is not supported in Umbraco 7, consider upgrading to the new v7 APIs"; + } + }; + //defined getters that will throw a not implemented/supported exception + Object.defineProperty(legacyNode, "menu", { + get: function () { + throw "'menu' property is not supported in Umbraco 7, consider upgrading to the new v7 APIs"; + } + }); + Object.defineProperty(legacyNode, "jsNode", { + get: function () { + throw "'jsNode' property is not supported in Umbraco 7, consider upgrading to the new v7 APIs"; + } + }); + Object.defineProperty(legacyNode, "jsTree", { + get: function () { + throw "'jsTree' property is not supported in Umbraco 7, consider upgrading to the new v7 APIs"; + } + }); + + return legacyNode; + } + }; + + return tree; + }, + appActions: function() { + var injector = getRootInjector(); + var navService = injector.get("navigationService"); + + var _actions = {}; + _actions.openDashboard = function(section){ + navService.changeSection(section); + }; + + return _actions; + //throw "Not implemented!"; + + ////if the main window has no actions, we'll create some + //if (this._appActions == null) { + // if (typeof this.mainWindow().appActions == 'undefined') { + // this._appActions = new Umbraco.Application.Actions(); + // } + // else this._appActions = this.mainWindow().appActions; + //} + //return this._appActions; + }, + uiKeys: function() { + + throw "Not implemented!"; + + ////TODO: If there is no main window, we need to go retrieve the appActions from the server! + //return this.mainWindow().uiKeys; + }, + contentFrameAndSection: function(app, rightFrameUrl) { + + throw "Not implemented!"; + + ////this.appActions().shiftApp(app, this.uiKeys()['sections_' + app]); + //var self = this; + //self.mainWindow().UmbClientMgr.historyManager().addHistory(app, true); + //window.setTimeout(function() { + // self.mainWindow().UmbClientMgr.contentFrame(rightFrameUrl); + //}, 200); + }, + + /** + * @ngdoc function + * @name contentFrame + * @methodOf UmbClientMgr + * @function + * + * @description + * This will tell our angular app to create and load in an iframe at the specified location + * @param strLocation {String} The URL to load the iframe in + */ + contentFrame: function (strLocation) { + + if (!strLocation || strLocation == "") { + //SD: NOTE: We used to return the content iframe object but now I'm not sure we should do that ?! + + if (typeof top.right != "undefined") { + return top.right; + } + else { + return top; //return the current window if the content frame doesn't exist in the current context + } + //return; + } + + this._debug("contentFrame: " + strLocation); + + //get our angular navigation service + var injector = getRootInjector(); + var navService = injector.get("navigationService"); + + //if the path doesn't start with "/" or with the root path then + //prepend the root path + if (!strLocation.startsWith("/")) { + strLocation = this._rootPath + "/" + strLocation; + } + else if (strLocation.length >= this._rootPath.length + && strLocation.substr(0, this._rootPath.length) != this._rootPath) { + strLocation = this._rootPath + "/" + strLocation; + } + + navService.loadLegacyIFrame(strLocation); + + }, + + getFakeFrame : function() { + return fakeFrame; + }, + + /** This is used to launch an angular based modal window instead of the legacy window */ + openAngularModalWindow: function (options) { + + //get our angular navigation service + var injector = getRootInjector(); + var dialogService = injector.get("dialogService"); + + var dialog = dialogService.open(options); + + ////add the callback to the jquery data for the modal so we can call it on close to support the legacy way dialogs worked. + //dialog.element.data("modalCb", onCloseCallback); + + //this._modal.push(dialog); + //return dialog; + }, + + openModalWindow: function(url, name, showHeader, width, height, top, leftOffset, closeTriggers, onCloseCallback) { + //need to create the modal on the top window if the top window has a client manager, if not, create it on the current window + + //get our angular navigation service + var injector = getRootInjector(); + var navService = injector.get("navigationService"); + var dialogService = injector.get("dialogService"); + + var self = this; + + //based on what state the nav ui is in, depends on how we are going to launch a model / dialog. A modal + // will show up on the right hand side and a dialog will show up as if it is in the menu. + // with the legacy API we cannot know what is expected so we can only check if the menu is active, if it is + // we'll launch a dialog, otherwise a modal. + var dialog; + if (navService.ui.currentMode === "menu") { + dialog = navService.showDialog({ + //create a 'fake' action to passin with the specified actionUrl since it needs to load into an iframe + action: { + name: name, + metaData: { + actionUrl: url + } + }, + node: {} + }); + } + else { + dialog = dialogService.open({ + template: url, + width: width, + height: height, + iframe: true, + show: true + }); + } + + + //add the callback to the jquery data for the modal so we can call it on close to support the legacy way dialogs worked. + dialog.element.data("modalCb", onCloseCallback); + //add the close triggers + if (angular.isArray(closeTriggers)) { + for (var i = 0; i < closeTriggers.length; i++) { + var e = dialog.find(closeTriggers[i]); + if (e.length > 0) { + e.click(function () { + self.closeModalWindow(); + }); + } + } + } + + this._modal.push(dialog); + return dialog; + }, + closeModalWindow: function(rVal) { + + //get our angular navigation service + var injector = getRootInjector(); + var dialogService = injector.get("dialogService"); + + // all legacy calls to closeModalWindow are expecting to just close the last opened one so we'll ensure + // that this is still the case. + if (this._modal != null && this._modal.length > 0) { + + var lastModal = this._modal.pop(); + + //if we've stored a callback on this modal call it before we close. + var self = this; + //get the compat callback from the modal element + var onCloseCallback = lastModal.element.data("modalCb"); + if (typeof onCloseCallback == "function") { + onCloseCallback.apply(self, [{ outVal: rVal }]); + } + + //just call the native dialog close() method to remove the dialog + lastModal.close(); + } + else { + //instead of calling just the dialog service we funnel it through the global + //event emitter + getRootScope().$emit("closeDialogs", event); + } + }, + _debug: function(strMsg) { + if (this._isDebug) { + Sys.Debug.trace("UmbClientMgr: " + strMsg); + } + }, + get_isDirty: function() { + return this._isDirty; + }, + set_isDirty: function(value) { + this._isDirty = value; + } + }; + }; +})(jQuery); + +//define alias for use throughout application +var UmbClientMgr = new Umbraco.Application.ClientManager(); diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index dc65e1328d..f5f6ae1bb2 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -1,48 +1,48 @@ -{ - "author": "Umbraco HQ", - "name": "umbraco", - "homepage": "https://github.com/umbraco/umbraco-cms/tree/7.0.0", - "version": "7.0.0-Beta", - "repository": { - "type": "git", - "url": "git@github.com:umbraco/umbraco-cms.git" - }, - "bugs": { - "url": "https://issues.umbraco.org" - }, - "licenses": [ - { - "type": "MIT", - "url": "https://github.com/umbraco/Umbraco-CMS/blob/7.0.0/docs/License.txt" - } - ], - "engines": { - "node": ">= 0.8.4" - }, - "dependencies": {}, - "devDependencies": { - "grunt": "~0.4.0", - "phantomjs": "~1.9.1-0", - "grunt-recess": "~0.3", - "grunt-contrib-clean": "~0.4.0", - "grunt-contrib-copy": "~0.4.0", - "grunt-contrib-jshint": "~0.2.0", - "grunt-contrib-concat": "~0.1.3", - "grunt-contrib-uglify": "~0.1.1", - "grunt-html2js": "~0.1.0", - "grunt-contrib-watch": "~0.3.1", - "grunt-open": "~0.2.0", - "grunt-contrib-connect": "~0.3.0", - "grunt-karma": "~0.5", - "karma-chrome-launcher": "0.0.2", - "karma-script-launcher": "0.0.1", - "karma-firefox-launcher": "0.0.2", - "karma-jasmine": "0.0.1", - "karma-requirejs": "0.0.1", - "karma-coffee-preprocessor": "0.0.1", - "karma": "~0.9", - "karma-phantomjs-launcher": "0.0.2", - "grunt-ngdocs": "~0.1.2", - "grunt-ngmin": "0.0.3" - } -} +{ + "author": "Umbraco HQ", + "name": "umbraco", + "homepage": "https://github.com/umbraco/umbraco-cms/tree/7.0.0", + "version": "7.0.0-Beta", + "repository": { + "type": "git", + "url": "git@github.com:umbraco/umbraco-cms.git" + }, + "bugs": { + "url": "https://issues.umbraco.org" + }, + "licenses": [ + { + "type": "MIT", + "url": "https://github.com/umbraco/Umbraco-CMS/blob/7.0.0/docs/License.txt" + } + ], + "engines": { + "node": ">= 0.8.4" + }, + "dependencies": {}, + "devDependencies": { + "grunt": "~0.4.0", + "phantomjs": "~1.9.1-0", + "grunt-recess": "~0.3", + "grunt-contrib-clean": "~0.4.0", + "grunt-contrib-copy": "~0.4.0", + "grunt-contrib-jshint": "~0.2.0", + "grunt-contrib-concat": "~0.1.3", + "grunt-contrib-uglify": "~0.1.1", + "grunt-html2js": "~0.1.0", + "grunt-contrib-watch": "~0.3.1", + "grunt-open": "~0.2.0", + "grunt-contrib-connect": "~0.3.0", + "grunt-karma": "~0.5", + "karma-chrome-launcher": "0.0.2", + "karma-script-launcher": "0.0.1", + "karma-firefox-launcher": "0.0.2", + "karma-jasmine": "0.0.1", + "karma-requirejs": "0.0.1", + "karma-coffee-preprocessor": "0.0.1", + "karma": "~0.9", + "karma-phantomjs-launcher": "0.0.2", + "grunt-ngdocs": "~0.1.2", + "grunt-ngmin": "0.0.3" + } +} diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/editors/umbcontentname.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/editors/umbcontentname.directive.js index 6220a795cc..88463ed8f5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/editors/umbcontentname.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/editors/umbcontentname.directive.js @@ -1,51 +1,51 @@ -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbContentName -* @restrict E -* @function -* @description -* Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form. -**/ -angular.module("umbraco.directives") - .directive('umbContentName', function ($timeout, localizationService) { - return { - require: "ngModel", - restrict: 'E', - replace: true, - templateUrl: 'views/directives/umb-content-name.html', - scope: { - placeholder: '@placeholder', - model: '=ngModel' - }, - link: function(scope, element, attrs, ngModel) { - - var inputElement = element.find("input"); - if(scope.placeholder && scope.placeholder[0] === "@"){ - localizationService.localize(scope.placeholder.substring(1)) - .then(function(value){ - scope.placeholder = value; - }); - } - - $timeout(function(){ - if(!scope.model){ - scope.goEdit(); - } - }, 100, false); - - scope.goEdit = function(){ - scope.editMode = true; - - $timeout(function () { - inputElement.focus(); - }, 100, false); - }; - - scope.exitEdit = function(){ - if(scope.model && scope.model !== ""){ - scope.editMode = false; - } - }; - } - }; +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbContentName +* @restrict E +* @function +* @description +* Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form. +**/ +angular.module("umbraco.directives") + .directive('umbContentName', function ($timeout, localizationService) { + return { + require: "ngModel", + restrict: 'E', + replace: true, + templateUrl: 'views/directives/umb-content-name.html', + scope: { + placeholder: '@placeholder', + model: '=ngModel' + }, + link: function(scope, element, attrs, ngModel) { + + var inputElement = element.find("input"); + if(scope.placeholder && scope.placeholder[0] === "@"){ + localizationService.localize(scope.placeholder.substring(1)) + .then(function(value){ + scope.placeholder = value; + }); + } + + $timeout(function(){ + if(!scope.model){ + scope.goEdit(); + } + }, 100, false); + + scope.goEdit = function(){ + scope.editMode = true; + + $timeout(function () { + inputElement.focus(); + }, 100, false); + }; + + scope.exitEdit = function(){ + if(scope.model && scope.model !== ""){ + scope.editMode = false; + } + }; + } + }; }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/editors/umbsort.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/editors/umbsort.directive.js index 1c9e851130..6166637eed 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/editors/umbsort.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/editors/umbsort.directive.js @@ -1,168 +1,168 @@ -/** - * @ngdoc directive - * @name umbraco.directives.directive:umbSort - * @element div - * @function - * - * @description - * Resize div's automatically to fit to the bottom of the screen, as an optional parameter an y-axis offset can be set - * So if you only want to scale the div to 70 pixels from the bottom you pass "70" - * - * @example - - -
    -
    -
    - */ -angular.module("umbraco.directives") - .value('umbSortContextInternal',{}) - .directive('umbSort', function($log,umbSortContextInternal) { - return { - require: '?ngModel', - link: function(scope, element, attrs, ngModel) { - var adjustment; - - var cfg = scope.$eval(element.attr('umb-sort')) || {}; - - scope.model = ngModel; - - scope.opts = cfg; - scope.opts.containerSelector= cfg.containerSelector || ".umb-" + cfg.group + "-container", - scope.opts.nested= cfg.nested || true, - scope.opts.drop= cfg.drop || true, - scope.opts.drag= cfg.drag || true, - scope.opts.clone = cfg.clone || "
  • "; - scope.opts.mode = cfg.mode || "list"; - - scope.opts.itemSelectorFull = $.trim(scope.opts.itemPath + " " + scope.opts.itemSelector); - - /* - scope.opts.isValidTarget = function(item, container) { - if(container.el.is(".umb-" + scope.opts.group + "-container")){ - return true; - } - return false; - }; - */ - - element.addClass("umb-sort"); - element.addClass("umb-" + cfg.group + "-container"); - - scope.opts.onDrag = function (item, position) { - if(scope.opts.mode === "list"){ - item.css({ - left: position.left - adjustment.left, - top: position.top - adjustment.top - }); - } - }; - - - scope.opts.onDrop = function (item, targetContainer, _super) { - - if(scope.opts.mode === "list"){ - //list mode - var clonedItem = $(scope.opts.clone).css({height: 0}); - item.after(clonedItem); - clonedItem.animate({'height': item.height()}); - - item.animate(clonedItem.position(), function () { - clonedItem.detach(); - _super(item); - }); - } - - var children = $(scope.opts.itemSelectorFull, targetContainer.el); - var targetIndex = children.index(item); - var targetScope = $(targetContainer.el[0]).scope(); - - - if(targetScope === umbSortContextInternal.sourceScope){ - if(umbSortContextInternal.sourceScope.opts.onSortHandler){ - var _largs = { - oldIndex: umbSortContextInternal.sourceIndex, - newIndex: targetIndex, - scope: umbSortContextInternal.sourceScope - }; - - umbSortContextInternal.sourceScope.opts.onSortHandler.call(this, item, _largs); - } - }else{ - - - if(targetScope.opts.onDropHandler){ - var args = { - sourceScope: umbSortContextInternal.sourceScope, - sourceIndex: umbSortContextInternal.sourceIndex, - sourceContainer: umbSortContextInternal.sourceContainer, - - targetScope: targetScope, - targetIndex: targetIndex, - targetContainer: targetContainer - }; - - targetScope.opts.onDropHandler.call(this, item, args); - } - - if(umbSortContextInternal.sourceScope.opts.onReleaseHandler){ - var _args = { - sourceScope: umbSortContextInternal.sourceScope, - sourceIndex: umbSortContextInternal.sourceIndex, - sourceContainer: umbSortContextInternal.sourceContainer, - - targetScope: targetScope, - targetIndex: targetIndex, - targetContainer: targetContainer - }; - - umbSortContextInternal.sourceScope.opts.onReleaseHandler.call(this, item, _args); - } - } - }; - - scope.changeIndex = function(from, to){ - scope.$apply(function(){ - var i = ngModel.$modelValue.splice(from, 1)[0]; - ngModel.$modelValue.splice(to, 0, i); - }); - }; - - scope.move = function(args){ - var from = args.sourceIndex; - var to = args.targetIndex; - - if(args.sourceContainer === args.targetContainer){ - scope.changeIndex(from, to); - }else{ - scope.$apply(function(){ - var i = args.sourceScope.model.$modelValue.splice(from, 1)[0]; - args.targetScope.model.$modelvalue.splice(to,0, i); - }); - } - }; - - scope.opts.onDragStart = function (item, container, _super) { - var children = $(scope.opts.itemSelectorFull, container.el); - var offset = item.offset(); - - umbSortContextInternal.sourceIndex = children.index(item); - umbSortContextInternal.sourceScope = $(container.el[0]).scope(); - umbSortContextInternal.sourceContainer = container; - - //current.item = ngModel.$modelValue.splice(current.index, 1)[0]; - - var pointer = container.rootGroup.pointer; - adjustment = { - left: pointer.left - offset.left, - top: pointer.top - offset.top - }; - - _super(item, container); - }; - - element.sortable( scope.opts ); - } - }; - +/** + * @ngdoc directive + * @name umbraco.directives.directive:umbSort + * @element div + * @function + * + * @description + * Resize div's automatically to fit to the bottom of the screen, as an optional parameter an y-axis offset can be set + * So if you only want to scale the div to 70 pixels from the bottom you pass "70" + * + * @example + + +
    +
    +
    + */ +angular.module("umbraco.directives") + .value('umbSortContextInternal',{}) + .directive('umbSort', function($log,umbSortContextInternal) { + return { + require: '?ngModel', + link: function(scope, element, attrs, ngModel) { + var adjustment; + + var cfg = scope.$eval(element.attr('umb-sort')) || {}; + + scope.model = ngModel; + + scope.opts = cfg; + scope.opts.containerSelector= cfg.containerSelector || ".umb-" + cfg.group + "-container", + scope.opts.nested= cfg.nested || true, + scope.opts.drop= cfg.drop || true, + scope.opts.drag= cfg.drag || true, + scope.opts.clone = cfg.clone || "
  • "; + scope.opts.mode = cfg.mode || "list"; + + scope.opts.itemSelectorFull = $.trim(scope.opts.itemPath + " " + scope.opts.itemSelector); + + /* + scope.opts.isValidTarget = function(item, container) { + if(container.el.is(".umb-" + scope.opts.group + "-container")){ + return true; + } + return false; + }; + */ + + element.addClass("umb-sort"); + element.addClass("umb-" + cfg.group + "-container"); + + scope.opts.onDrag = function (item, position) { + if(scope.opts.mode === "list"){ + item.css({ + left: position.left - adjustment.left, + top: position.top - adjustment.top + }); + } + }; + + + scope.opts.onDrop = function (item, targetContainer, _super) { + + if(scope.opts.mode === "list"){ + //list mode + var clonedItem = $(scope.opts.clone).css({height: 0}); + item.after(clonedItem); + clonedItem.animate({'height': item.height()}); + + item.animate(clonedItem.position(), function () { + clonedItem.detach(); + _super(item); + }); + } + + var children = $(scope.opts.itemSelectorFull, targetContainer.el); + var targetIndex = children.index(item); + var targetScope = $(targetContainer.el[0]).scope(); + + + if(targetScope === umbSortContextInternal.sourceScope){ + if(umbSortContextInternal.sourceScope.opts.onSortHandler){ + var _largs = { + oldIndex: umbSortContextInternal.sourceIndex, + newIndex: targetIndex, + scope: umbSortContextInternal.sourceScope + }; + + umbSortContextInternal.sourceScope.opts.onSortHandler.call(this, item, _largs); + } + }else{ + + + if(targetScope.opts.onDropHandler){ + var args = { + sourceScope: umbSortContextInternal.sourceScope, + sourceIndex: umbSortContextInternal.sourceIndex, + sourceContainer: umbSortContextInternal.sourceContainer, + + targetScope: targetScope, + targetIndex: targetIndex, + targetContainer: targetContainer + }; + + targetScope.opts.onDropHandler.call(this, item, args); + } + + if(umbSortContextInternal.sourceScope.opts.onReleaseHandler){ + var _args = { + sourceScope: umbSortContextInternal.sourceScope, + sourceIndex: umbSortContextInternal.sourceIndex, + sourceContainer: umbSortContextInternal.sourceContainer, + + targetScope: targetScope, + targetIndex: targetIndex, + targetContainer: targetContainer + }; + + umbSortContextInternal.sourceScope.opts.onReleaseHandler.call(this, item, _args); + } + } + }; + + scope.changeIndex = function(from, to){ + scope.$apply(function(){ + var i = ngModel.$modelValue.splice(from, 1)[0]; + ngModel.$modelValue.splice(to, 0, i); + }); + }; + + scope.move = function(args){ + var from = args.sourceIndex; + var to = args.targetIndex; + + if(args.sourceContainer === args.targetContainer){ + scope.changeIndex(from, to); + }else{ + scope.$apply(function(){ + var i = args.sourceScope.model.$modelValue.splice(from, 1)[0]; + args.targetScope.model.$modelvalue.splice(to,0, i); + }); + } + }; + + scope.opts.onDragStart = function (item, container, _super) { + var children = $(scope.opts.itemSelectorFull, container.el); + var offset = item.offset(); + + umbSortContextInternal.sourceIndex = children.index(item); + umbSortContextInternal.sourceScope = $(container.el[0]).scope(); + umbSortContextInternal.sourceContainer = container; + + //current.item = ngModel.$modelValue.splice(current.index, 1)[0]; + + var pointer = container.rootGroup.pointer; + adjustment = { + left: pointer.left - offset.left, + top: pointer.top - offset.top + }; + + _super(item, container); + }; + + element.sortable( scope.opts ); + } + }; + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/fixnumber.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/fixnumber.directive.js index c177a60ec3..4ec30b285f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/fixnumber.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/fixnumber.directive.js @@ -1,60 +1,60 @@ - -/** -* @ngdoc directive -* @name umbraco.directives.directive:fixNumber -* @restrict A -* @description Used in conjunction with type='number' input fields to ensure that the bound value is converted to a number when using ng-model -* because normally it thinks it's a string and also validation doesn't work correctly due to an angular bug. -**/ -function fixNumber() { - return { - restrict: "A", - require: "ngModel", - link: function (scope, element, attr, ngModel) { - - //This fixes the issue of when your model contains a number as a string (i.e. "1" instead of 1) - // which will not actually work on initial load and the browser will say you have an invalid number - // entered. So if it parses to a number, we call setViewValue which sets the bound model value - // to the real number. It should in theory update the view but it doesn't so we need to manually set - // the element's value. I'm sure there's a bug logged for this somewhere for angular too. - - var modelVal = scope.$eval(attr.ngModel); - if (modelVal) { - var asNum = parseFloat(modelVal, 10); - if (!isNaN(asNum)) { - ngModel.$setViewValue(asNum); - element.val(asNum); - } - else { - ngModel.$setViewValue(null); - element.val(""); - } - } - - ngModel.$formatters.push(function (value) { - if (angular.isString(value)) { - return parseFloat(value); - } - return value; - }); - - //This fixes this angular issue: - //https://github.com/angular/angular.js/issues/2144 - // which doesn't actually validate the number input properly since the model only changes when a real number is entered - // but the input box still allows non-numbers to be entered which do not validate (only via html5) - - if (typeof element.prop('validity') === 'undefined') { - return; - } - - element.bind('input', function (e) { - var validity = element.prop('validity'); - scope.$apply(function () { - ngModel.$setValidity('number', !validity.badInput); - }); - }); - - } - }; -} + +/** +* @ngdoc directive +* @name umbraco.directives.directive:fixNumber +* @restrict A +* @description Used in conjunction with type='number' input fields to ensure that the bound value is converted to a number when using ng-model +* because normally it thinks it's a string and also validation doesn't work correctly due to an angular bug. +**/ +function fixNumber() { + return { + restrict: "A", + require: "ngModel", + link: function (scope, element, attr, ngModel) { + + //This fixes the issue of when your model contains a number as a string (i.e. "1" instead of 1) + // which will not actually work on initial load and the browser will say you have an invalid number + // entered. So if it parses to a number, we call setViewValue which sets the bound model value + // to the real number. It should in theory update the view but it doesn't so we need to manually set + // the element's value. I'm sure there's a bug logged for this somewhere for angular too. + + var modelVal = scope.$eval(attr.ngModel); + if (modelVal) { + var asNum = parseFloat(modelVal, 10); + if (!isNaN(asNum)) { + ngModel.$setViewValue(asNum); + element.val(asNum); + } + else { + ngModel.$setViewValue(null); + element.val(""); + } + } + + ngModel.$formatters.push(function (value) { + if (angular.isString(value)) { + return parseFloat(value); + } + return value; + }); + + //This fixes this angular issue: + //https://github.com/angular/angular.js/issues/2144 + // which doesn't actually validate the number input properly since the model only changes when a real number is entered + // but the input box still allows non-numbers to be entered which do not validate (only via html5) + + if (typeof element.prop('validity') === 'undefined') { + return; + } + + element.bind('input', function (e) { + var validity = element.prop('validity'); + scope.$apply(function () { + ngModel.$setValidity('number', !validity.badInput); + }); + }); + + } + }; +} angular.module('umbraco.directives').directive("fixNumber", fixNumber); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/html/umbcontrolgroup.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/html/umbcontrolgroup.directive.js index e9adfe16ea..3eec10f332 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/html/umbcontrolgroup.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/html/umbcontrolgroup.directive.js @@ -1,38 +1,38 @@ -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbProperty -* @restrict E -**/ -angular.module("umbraco.directives.html") - .directive('umbControlGroup', function (localizationService) { - return { - scope: { - label: "@label", - description: "@", - hideLabel: "@", - alias: "@" - }, - require: '?^form', - transclude: true, - restrict: 'E', - replace: true, - templateUrl: 'views/directives/html/umb-control-group.html', - link: function (scope, element, attr, formCtrl) { - - scope.formValid = function() { - if (formCtrl) { - return formCtrl.$valid; - } - //there is no form. - return true; - }; - - if (scope.label && scope.label[0] === "@") { - scope.labelstring = localizationService.localize(scope.label.substring(1)); - } - else { - scope.labelstring = scope.label; - } - } - }; +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbProperty +* @restrict E +**/ +angular.module("umbraco.directives.html") + .directive('umbControlGroup', function (localizationService) { + return { + scope: { + label: "@label", + description: "@", + hideLabel: "@", + alias: "@" + }, + require: '?^form', + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/directives/html/umb-control-group.html', + link: function (scope, element, attr, formCtrl) { + + scope.formValid = function() { + if (formCtrl) { + return formCtrl.$valid; + } + //there is no form. + return true; + }; + + if (scope.label && scope.label[0] === "@") { + scope.labelstring = localizationService.localize(scope.label.substring(1)); + } + else { + scope.labelstring = scope.label; + } + } + }; }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/html/umbpanel.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/html/umbpanel.directive.js index 0b098fc44d..2d399a063b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/html/umbpanel.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/html/umbpanel.directive.js @@ -1,14 +1,14 @@ -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbPanel -* @restrict E -**/ -angular.module("umbraco.directives.html") - .directive('umbPanel', function(){ - return { - restrict: 'E', - replace: true, - transclude: 'true', - templateUrl: 'views/directives/html/umb-panel.html' - }; +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbPanel +* @restrict E +**/ +angular.module("umbraco.directives.html") + .directive('umbPanel', function(){ + return { + restrict: 'E', + replace: true, + transclude: 'true', + templateUrl: 'views/directives/html/umb-panel.html' + }; }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/html/umbphotofolder.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/html/umbphotofolder.directive.js index 266e34b563..49cea5dbf5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/html/umbphotofolder.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/html/umbphotofolder.directive.js @@ -1,167 +1,167 @@ -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbPhotoFolder -* @restrict E -**/ -angular.module("umbraco.directives.html") -.directive('umbPhotoFolder', function ($compile, $log, $timeout, $filter, imageHelper, umbRequestHelper) { - - function renderCollection(scope, photos) { - // get row width - this is fixed. - var w = scope.lastWidth; - var rows = []; - - // initial height - effectively the maximum height +/- 10%; - var h = Math.max(scope.minHeight, Math.floor(w / 5)); - - // store relative widths of all images (scaled to match estimate height above) - var ws = []; - $.each(photos, function (key, val) { - - val.width_n = $.grep(val.properties, function (v, index) { return (v.alias === "umbracoWidth"); })[0]; - val.height_n = $.grep(val.properties, function (v, index) { return (v.alias === "umbracoHeight"); })[0]; - - //val.url_n = imageHelper.getThumbnail({ imageModel: val, scope: scope }); - - if (val.width_n && val.height_n) { - var wt = parseInt(val.width_n.value, 10); - var ht = parseInt(val.height_n.value, 10); - - if (ht !== h) { - wt = Math.floor(wt * (h / ht)); - } - - ws.push(wt); - } else { - //if its files or folders, we make them square - ws.push(scope.minHeight); - } - }); - - - var rowNum = 0; - var limit = photos.length; - while (scope.baseline < limit) { - rowNum++; - // number of images appearing in this row - var c = 0; - // total width of images in this row - including margins - var tw = 0; - - // calculate width of images and number of images to view in this row. - while ((tw * 1.1 < w) && (scope.baseline + c < limit)) { - tw += ws[scope.baseline + c++] + scope.border * 2; - } - - // Ratio of actual width of row to total width of images to be used. - var r = w / tw; - // image number being processed - var i = 0; - // reset total width to be total width of processed images - tw = 0; - - // new height is not original height * ratio - var ht = Math.floor(h * r); - - var row = {}; - row.photos = []; - row.style = {}; - row.style = { "height": ht + scope.border * 2, "width": scope.lastWidth }; - rows.push(row); - - while (i < c) { - var photo = photos[scope.baseline + i]; - // Calculate new width based on ratio - var wt = Math.floor(ws[scope.baseline + i] * r); - // add to total width with margins - tw += wt + scope.border * 2; - - //get the image property (if one exists) - var imageProp = imageHelper.getImagePropertyValue({ imageModel: photo }); - if (!imageProp) { - //TODO: Do something better than this!! - photo.thumbnail = "none"; - } - else { - - //get the proxy url for big thumbnails (this ensures one is always generated) - var thumbnailUrl = umbRequestHelper.getApiUrl( - "mediaApiBaseUrl", - "GetBigThumbnail", - [{ mediaId: photo.id }]); - photo.thumbnail = thumbnailUrl; - } - - photo.style = { "width": wt, "height": ht, "margin": scope.border + "px", "cursor": "pointer" }; - row.photos.push(photo); - i++; - } - - // set row height to actual height + margins - scope.baseline += c; - - // if total width is slightly smaller than - // actual div width then add 1 to each - // photo width till they match - i = 0; - while (tw < w) { - row.photos[i].style.width++; - i = (i + 1) % c; - tw++; - } - - // if total width is slightly bigger than - // actual div width then subtract 1 from each - // photo width till they match - i = 0; - while (tw > w) { - row.photos[i].style.width--; - i = (i + 1) % c; - tw--; - } - } - - return rows; - } - - return { - restrict: 'E', - replace: true, - require: '?ngModel', - terminate: true, - templateUrl: 'views/directives/html/umb-photo-folder.html', - link: function (scope, element, attrs, ngModel) { - - ngModel.$render = function () { - if (ngModel.$modelValue) { - - $timeout(function () { - var photos = ngModel.$modelValue; - - scope.baseline = element.attr('baseline') ? parseInt(element.attr('baseline'), 10) : 0; - scope.minWidth = element.attr('min-width') ? parseInt(element.attr('min-width'), 10) : 420; - scope.minHeight = element.attr('min-height') ? parseInt(element.attr('min-height'), 10) : 200; - scope.border = element.attr('border') ? parseInt(element.attr('border'), 10) : 5; - scope.clickHandler = scope.$eval(element.attr('on-click')); - scope.lastWidth = Math.max(element.width(), scope.minWidth); - - scope.rows = renderCollection(scope, photos); - - if (attrs.filterBy) { - scope.$watch(attrs.filterBy, function (newVal, oldVal) { - if (newVal !== oldVal) { - var p = $filter('filter')(photos, newVal, false); - scope.baseline = 0; - var m = renderCollection(scope, p); - scope.rows = m; - } - }); - } - - }, 500); //end timeout - } //end if modelValue - - }; //end $render - } - }; -}); +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbPhotoFolder +* @restrict E +**/ +angular.module("umbraco.directives.html") +.directive('umbPhotoFolder', function ($compile, $log, $timeout, $filter, imageHelper, umbRequestHelper) { + + function renderCollection(scope, photos) { + // get row width - this is fixed. + var w = scope.lastWidth; + var rows = []; + + // initial height - effectively the maximum height +/- 10%; + var h = Math.max(scope.minHeight, Math.floor(w / 5)); + + // store relative widths of all images (scaled to match estimate height above) + var ws = []; + $.each(photos, function (key, val) { + + val.width_n = $.grep(val.properties, function (v, index) { return (v.alias === "umbracoWidth"); })[0]; + val.height_n = $.grep(val.properties, function (v, index) { return (v.alias === "umbracoHeight"); })[0]; + + //val.url_n = imageHelper.getThumbnail({ imageModel: val, scope: scope }); + + if (val.width_n && val.height_n) { + var wt = parseInt(val.width_n.value, 10); + var ht = parseInt(val.height_n.value, 10); + + if (ht !== h) { + wt = Math.floor(wt * (h / ht)); + } + + ws.push(wt); + } else { + //if its files or folders, we make them square + ws.push(scope.minHeight); + } + }); + + + var rowNum = 0; + var limit = photos.length; + while (scope.baseline < limit) { + rowNum++; + // number of images appearing in this row + var c = 0; + // total width of images in this row - including margins + var tw = 0; + + // calculate width of images and number of images to view in this row. + while ((tw * 1.1 < w) && (scope.baseline + c < limit)) { + tw += ws[scope.baseline + c++] + scope.border * 2; + } + + // Ratio of actual width of row to total width of images to be used. + var r = w / tw; + // image number being processed + var i = 0; + // reset total width to be total width of processed images + tw = 0; + + // new height is not original height * ratio + var ht = Math.floor(h * r); + + var row = {}; + row.photos = []; + row.style = {}; + row.style = { "height": ht + scope.border * 2, "width": scope.lastWidth }; + rows.push(row); + + while (i < c) { + var photo = photos[scope.baseline + i]; + // Calculate new width based on ratio + var wt = Math.floor(ws[scope.baseline + i] * r); + // add to total width with margins + tw += wt + scope.border * 2; + + //get the image property (if one exists) + var imageProp = imageHelper.getImagePropertyValue({ imageModel: photo }); + if (!imageProp) { + //TODO: Do something better than this!! + photo.thumbnail = "none"; + } + else { + + //get the proxy url for big thumbnails (this ensures one is always generated) + var thumbnailUrl = umbRequestHelper.getApiUrl( + "mediaApiBaseUrl", + "GetBigThumbnail", + [{ mediaId: photo.id }]); + photo.thumbnail = thumbnailUrl; + } + + photo.style = { "width": wt, "height": ht, "margin": scope.border + "px", "cursor": "pointer" }; + row.photos.push(photo); + i++; + } + + // set row height to actual height + margins + scope.baseline += c; + + // if total width is slightly smaller than + // actual div width then add 1 to each + // photo width till they match + i = 0; + while (tw < w) { + row.photos[i].style.width++; + i = (i + 1) % c; + tw++; + } + + // if total width is slightly bigger than + // actual div width then subtract 1 from each + // photo width till they match + i = 0; + while (tw > w) { + row.photos[i].style.width--; + i = (i + 1) % c; + tw--; + } + } + + return rows; + } + + return { + restrict: 'E', + replace: true, + require: '?ngModel', + terminate: true, + templateUrl: 'views/directives/html/umb-photo-folder.html', + link: function (scope, element, attrs, ngModel) { + + ngModel.$render = function () { + if (ngModel.$modelValue) { + + $timeout(function () { + var photos = ngModel.$modelValue; + + scope.baseline = element.attr('baseline') ? parseInt(element.attr('baseline'), 10) : 0; + scope.minWidth = element.attr('min-width') ? parseInt(element.attr('min-width'), 10) : 420; + scope.minHeight = element.attr('min-height') ? parseInt(element.attr('min-height'), 10) : 200; + scope.border = element.attr('border') ? parseInt(element.attr('border'), 10) : 5; + scope.clickHandler = scope.$eval(element.attr('on-click')); + scope.lastWidth = Math.max(element.width(), scope.minWidth); + + scope.rows = renderCollection(scope, photos); + + if (attrs.filterBy) { + scope.$watch(attrs.filterBy, function (newVal, oldVal) { + if (newVal !== oldVal) { + var p = $filter('filter')(photos, newVal, false); + scope.baseline = 0; + var m = renderCollection(scope, p); + scope.rows = m; + } + }); + } + + }, 500); //end timeout + } //end if modelValue + + }; //end $render + } + }; +}); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/umbItemSorter.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/umbItemSorter.directive.js index 9d6fd057a2..8c6d47628c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/umbItemSorter.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/umbItemSorter.directive.js @@ -1,66 +1,66 @@ -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbItemSorter -* @function -* @element ANY -* @restrict E -* @description A re-usable directive for sorting items -**/ -function umbItemSorter(angularHelper) { - return { - scope: { - model: "=" - }, - restrict: "E", // restrict to an element - replace: true, // replace the html element with the template - templateUrl: 'views/directives/umb-item-sorter.html', - link: function(scope, element, attrs, ctrl) { - var defaultModel = { - okButton: "Ok", - successMsg: "Sorting successful", - complete: false - }; - //assign user vals to default - angular.extend(defaultModel, scope.model); - //re-assign merged to user - scope.model = defaultModel; - - scope.performSort = function() { - scope.$emit("umbItemSorter.sorting", { - sortedItems: scope.model.itemsToSort - }); - }; - - scope.handleCancel = function () { - scope.$emit("umbItemSorter.cancel"); - }; - - scope.handleOk = function() { - scope.$emit("umbItemSorter.ok"); - }; - - //defines the options for the jquery sortable - scope.sortableOptions = { - axis: 'y', - cursor: "move", - placeholder: "ui-sortable-placeholder", - update: function (ev, ui) { - //highlight the item when the position is changed - $(ui.item).effect("highlight", { color: "#049cdb" }, 500); - }, - stop: function (ev, ui) { - //the ui-sortable directive already ensures that our list is re-sorted, so now we just - // need to update the sortOrder to the index of each item - angularHelper.safeApply(scope, function () { - angular.forEach(scope.itemsToSort, function (val, index) { - val.sortOrder = index + 1; - }); - - }); - } - }; - } - }; -} - -angular.module('umbraco.directives').directive("umbItemSorter", umbItemSorter); +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbItemSorter +* @function +* @element ANY +* @restrict E +* @description A re-usable directive for sorting items +**/ +function umbItemSorter(angularHelper) { + return { + scope: { + model: "=" + }, + restrict: "E", // restrict to an element + replace: true, // replace the html element with the template + templateUrl: 'views/directives/umb-item-sorter.html', + link: function(scope, element, attrs, ctrl) { + var defaultModel = { + okButton: "Ok", + successMsg: "Sorting successful", + complete: false + }; + //assign user vals to default + angular.extend(defaultModel, scope.model); + //re-assign merged to user + scope.model = defaultModel; + + scope.performSort = function() { + scope.$emit("umbItemSorter.sorting", { + sortedItems: scope.model.itemsToSort + }); + }; + + scope.handleCancel = function () { + scope.$emit("umbItemSorter.cancel"); + }; + + scope.handleOk = function() { + scope.$emit("umbItemSorter.ok"); + }; + + //defines the options for the jquery sortable + scope.sortableOptions = { + axis: 'y', + cursor: "move", + placeholder: "ui-sortable-placeholder", + update: function (ev, ui) { + //highlight the item when the position is changed + $(ui.item).effect("highlight", { color: "#049cdb" }, 500); + }, + stop: function (ev, ui) { + //the ui-sortable directive already ensures that our list is re-sorted, so now we just + // need to update the sortOrder to the index of each item + angularHelper.safeApply(scope, function () { + angular.forEach(scope.itemsToSort, function (val, index) { + val.sortOrder = index + 1; + }); + + }); + } + }; + } + }; +} + +angular.module('umbraco.directives').directive("umbItemSorter", umbItemSorter); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/umbsections.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/umbsections.directive.js index a1eb2b06bf..f1aeeb84e3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/umbsections.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/umbsections.directive.js @@ -1,78 +1,78 @@ -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbSections -* @restrict E -**/ -function sectionsDirective($timeout, $window, navigationService, treeService, sectionResource) { - return { - restrict: "E", // restrict to an element - replace: true, // replace the html element with the template - templateUrl: 'views/directives/umb-sections.html', - link: function (scope, element, attr, ctrl) { - - scope.maxSections = 7; - scope.overflowingSections = 0; - scope.sections = []; - scope.nav = navigationService; - - - function loadSections(){ - sectionResource.getSections() - .then(function (result) { - scope.sections = result; - calculateHeight(); - }); - } - - function calculateHeight(){ - $timeout(function(){ - //total height minus room for avatar and help icon - var height = $(window).height()-200; - scope.totalSections = scope.sections.length; - scope.maxSections = Math.floor(height / 70); - scope.needTray = false; - - if(scope.totalSections > scope.maxSections){ - scope.needTray = true; - scope.overflowingSections = scope.maxSections - scope.totalSections; - } - }); - } - - //When the user logs in - scope.$on("authenticated", function (evt, data) { - //populate their sections if the user has changed - if (data.lastUserId !== data.user.id) { - loadSections(); - } - }); - - //on page resize - window.onresize = calculateHeight; - - scope.avatarClick = function(){ - navigationService.showUserDialog(); - }; - - scope.helpClick = function(){ - navigationService.showHelpDialog(); - }; - - scope.sectionClick = function (section) { - navigationService.hideSearch(); - navigationService.showTree(section.alias); - }; - - scope.sectionDblClick = function(section){ - navigationService.reloadSection(section.alias); - }; - - scope.trayClick = function(){ - navigationService.showTray(); - }; - - } - }; -} - -angular.module('umbraco.directives').directive("umbSections", sectionsDirective); +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbSections +* @restrict E +**/ +function sectionsDirective($timeout, $window, navigationService, treeService, sectionResource) { + return { + restrict: "E", // restrict to an element + replace: true, // replace the html element with the template + templateUrl: 'views/directives/umb-sections.html', + link: function (scope, element, attr, ctrl) { + + scope.maxSections = 7; + scope.overflowingSections = 0; + scope.sections = []; + scope.nav = navigationService; + + + function loadSections(){ + sectionResource.getSections() + .then(function (result) { + scope.sections = result; + calculateHeight(); + }); + } + + function calculateHeight(){ + $timeout(function(){ + //total height minus room for avatar and help icon + var height = $(window).height()-200; + scope.totalSections = scope.sections.length; + scope.maxSections = Math.floor(height / 70); + scope.needTray = false; + + if(scope.totalSections > scope.maxSections){ + scope.needTray = true; + scope.overflowingSections = scope.maxSections - scope.totalSections; + } + }); + } + + //When the user logs in + scope.$on("authenticated", function (evt, data) { + //populate their sections if the user has changed + if (data.lastUserId !== data.user.id) { + loadSections(); + } + }); + + //on page resize + window.onresize = calculateHeight; + + scope.avatarClick = function(){ + navigationService.showUserDialog(); + }; + + scope.helpClick = function(){ + navigationService.showHelpDialog(); + }; + + scope.sectionClick = function (section) { + navigationService.hideSearch(); + navigationService.showTree(section.alias); + }; + + scope.sectionDblClick = function(section){ + navigationService.reloadSection(section.alias); + }; + + scope.trayClick = function(){ + navigationService.showTray(); + }; + + } + }; +} + +angular.module('umbraco.directives').directive("umbSections", sectionsDirective); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valHighlight.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valHighlight.directive.js index 5d4b6cf9f9..b276c8c931 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valHighlight.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valHighlight.directive.js @@ -1,30 +1,30 @@ -/** -* @ngdoc directive -* @name umbraco.directives.directive:valHighlight -* @restrict A -* @description Used on input fields when you want to signal that they are in error, this will highlight the item for 1 second -**/ -function valHighlight($timeout) { - return { - restrict: "A", - link: function (scope, element, attrs, ctrl) { - - scope.$watch(function() { - return scope.$eval(attrs.valHighlight); - }, function(newVal, oldVal) { - if (newVal === true) { - element.addClass("highlight-error"); - $timeout(function () { - //set the bound scope property to false - scope[attrs.valHighlight] = false; - }, 1000); - } - else { - element.removeClass("highlight-error"); - } - }); - - } - }; -} +/** +* @ngdoc directive +* @name umbraco.directives.directive:valHighlight +* @restrict A +* @description Used on input fields when you want to signal that they are in error, this will highlight the item for 1 second +**/ +function valHighlight($timeout) { + return { + restrict: "A", + link: function (scope, element, attrs, ctrl) { + + scope.$watch(function() { + return scope.$eval(attrs.valHighlight); + }, function(newVal, oldVal) { + if (newVal === true) { + element.addClass("highlight-error"); + $timeout(function () { + //set the bound scope property to false + scope[attrs.valHighlight] = false; + }, 1000); + } + else { + element.removeClass("highlight-error"); + } + }); + + } + }; +} angular.module('umbraco.directives').directive("valHighlight", valHighlight); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valcompare.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valcompare.directive.js index 1a36dcc24f..31595273de 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valcompare.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valcompare.directive.js @@ -1,24 +1,24 @@ -angular.module('umbraco.directives.validation') - .directive('valCompare',function () { - return { - require: "ngModel", - link: function (scope, elem, attrs, ctrl) { - - //TODO: Pretty sure this should be done using a requires ^form in the directive declaration - var otherInput = elem.inheritedData("$formController")[attrs.valCompare]; - - ctrl.$parsers.push(function(value) { - if(value === otherInput.$viewValue) { - ctrl.$setValidity("valCompare", true); - return value; - } - ctrl.$setValidity("valCompare", false); - }); - - otherInput.$parsers.push(function(value) { - ctrl.$setValidity("valCompare", value === ctrl.$viewValue); - return value; - }); - } - }; +angular.module('umbraco.directives.validation') + .directive('valCompare',function () { + return { + require: "ngModel", + link: function (scope, elem, attrs, ctrl) { + + //TODO: Pretty sure this should be done using a requires ^form in the directive declaration + var otherInput = elem.inheritedData("$formController")[attrs.valCompare]; + + ctrl.$parsers.push(function(value) { + if(value === otherInput.$viewValue) { + ctrl.$setValidity("valCompare", true); + return value; + } + ctrl.$setValidity("valCompare", false); + }); + + otherInput.$parsers.push(function(value) { + ctrl.$setValidity("valCompare", value === ctrl.$viewValue); + return value; + }); + } + }; }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js index 1c002f2269..7d7246827e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js @@ -1,57 +1,57 @@ -/** -* @ngdoc directive -* @name umbraco.directives.directive:valFormManager -* @restrict A -* @require formController -* @description Used to broadcast an event to all elements inside this one to notify that form validation has -* changed. If we don't use this that means you have to put a watch for each directive on a form's validation -* changing which would result in much higher processing. We need to actually watch the whole $error collection of a form -* because just watching $valid or $invalid doesn't acurrately trigger form validation changing. -* This also sets the show-validation (or a custom) css class on the element when the form is invalid - this lets -* us css target elements to be displayed when the form is submitting/submitted. -* Another thing this directive does is to ensure that any .control-group that contains form elements that are invalid will -* be marked with the 'error' css class. This ensures that labels included in that control group are styled correctly. -**/ -function valFormManager(serverValidationManager) { - return { - require: "form", - restrict: "A", - link: function (scope, element, attr, formCtrl) { - - scope.$watch(function () { - return formCtrl.$error; - }, function (e) { - scope.$broadcast("valStatusChanged", { form: formCtrl }); - - //find all invalid elements' .control-group's and apply the error class - var inError = element.find(".control-group .ng-invalid").closest(".control-group"); - inError.addClass("error"); - - //find all control group's that have no error and ensure the class is removed - var noInError = element.find(".control-group .ng-valid").closest(".control-group").not(inError); - noInError.removeClass("error"); - - }, true); - - var className = attr.valShowValidation ? attr.valShowValidation : "show-validation"; - var savingEventName = attr.savingEvent ? attr.savingEvent : "formSubmitting"; - var savedEvent = attr.savedEvent ? attr.savingEvent : "formSubmitted"; - - //we should show validation if there are any msgs in the server validation collection - if (serverValidationManager.items.length > 0) { - element.addClass(className); - } - - //listen for the forms saving event - scope.$on(savingEventName, function (ev, args) { - element.addClass(className); - }); - - //listen for the forms saved event - scope.$on(savedEvent, function (ev, args) { - element.removeClass(className); - }); - } - }; -} +/** +* @ngdoc directive +* @name umbraco.directives.directive:valFormManager +* @restrict A +* @require formController +* @description Used to broadcast an event to all elements inside this one to notify that form validation has +* changed. If we don't use this that means you have to put a watch for each directive on a form's validation +* changing which would result in much higher processing. We need to actually watch the whole $error collection of a form +* because just watching $valid or $invalid doesn't acurrately trigger form validation changing. +* This also sets the show-validation (or a custom) css class on the element when the form is invalid - this lets +* us css target elements to be displayed when the form is submitting/submitted. +* Another thing this directive does is to ensure that any .control-group that contains form elements that are invalid will +* be marked with the 'error' css class. This ensures that labels included in that control group are styled correctly. +**/ +function valFormManager(serverValidationManager) { + return { + require: "form", + restrict: "A", + link: function (scope, element, attr, formCtrl) { + + scope.$watch(function () { + return formCtrl.$error; + }, function (e) { + scope.$broadcast("valStatusChanged", { form: formCtrl }); + + //find all invalid elements' .control-group's and apply the error class + var inError = element.find(".control-group .ng-invalid").closest(".control-group"); + inError.addClass("error"); + + //find all control group's that have no error and ensure the class is removed + var noInError = element.find(".control-group .ng-valid").closest(".control-group").not(inError); + noInError.removeClass("error"); + + }, true); + + var className = attr.valShowValidation ? attr.valShowValidation : "show-validation"; + var savingEventName = attr.savingEvent ? attr.savingEvent : "formSubmitting"; + var savedEvent = attr.savedEvent ? attr.savingEvent : "formSubmitted"; + + //we should show validation if there are any msgs in the server validation collection + if (serverValidationManager.items.length > 0) { + element.addClass(className); + } + + //listen for the forms saving event + scope.$on(savingEventName, function (ev, args) { + element.addClass(className); + }); + + //listen for the forms saved event + scope.$on(savedEvent, function (ev, args) { + element.removeClass(className); + }); + } + }; +} angular.module('umbraco.directives').directive("valFormManager", valFormManager); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js index 13e677b1bb..ea4b1b82c8 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js @@ -1,154 +1,154 @@ -/** -* @ngdoc directive -* @name umbraco.directives.directive:valPropertyMsg -* @restrict A -* @element textarea -* @requires formController -* @description This directive is used to control the display of the property level validation message. -* We will listen for server side validation changes -* and when an error is detected for this property we'll show the error message. -* In order for this directive to work, the valStatusChanged directive must be placed on the containing form. -**/ -function valPropertyMsg(serverValidationManager) { - return { - scope: { - property: "=property" - }, - require: "^form", //require that this directive is contained within an ngForm - replace: true, //replace the element with the template - restrict: "E", //restrict to element - template: "
    {{errorMsg}}
    ", - - /** - Our directive requries a reference to a form controller - which gets passed in to this parameter - */ - link: function (scope, element, attrs, formCtrl) { - - //if there's any remaining errors in the server validation service then we should show them. - var showValidation = serverValidationManager.items.length > 0; - var hasError = false; - - //create properties on our custom scope so we can use it in our template - scope.errorMsg = ""; - - //listen for form error changes - scope.$on("valStatusChanged", function(evt, args) { - if (args.form.$invalid) { - - //first we need to check if the valPropertyMsg validity is invalid - if (formCtrl.$error.valPropertyMsg && formCtrl.$error.valPropertyMsg.length > 0) { - //since we already have an error we'll just return since this means we've already set the - // hasError and errorMsg properties which occurs below in the serverValidationManager.subscribe - return; - } - else if (element.closest(".umb-control-group").find(".ng-invalid").length > 0) { - //check if it's one of the properties that is invalid in the current content property - hasError = true; - //update the validation message if we don't already have one assigned. - if (showValidation && scope.errorMsg === "") { - var err; - //this can be null if no property was assigned - if (scope.property) { - err = serverValidationManager.getPropertyError(scope.property.alias, ""); - } - scope.errorMsg = err ? err.errorMsg : "Property has errors"; - } - } - else { - hasError = false; - scope.errorMsg = ""; - } - } - else { - hasError = false; - scope.errorMsg = ""; - } - }, true); - - //listen for the forms saving event - scope.$on("formSubmitting", function (ev, args) { - showValidation = true; - if (hasError && scope.errorMsg === "") { - var err; - //this can be null if no property was assigned - if (scope.property) { - err = serverValidationManager.getPropertyError(scope.property.alias, ""); - } - scope.errorMsg = err ? err.errorMsg : "Property has errors"; - } - else if (!hasError) { - scope.errorMsg = ""; - } - }); - - //listen for the forms saved event - scope.$on("formSubmitted", function (ev, args) { - showValidation = false; - scope.errorMsg = ""; - formCtrl.$setValidity('valPropertyMsg', true); - }); - - //We need to subscribe to any changes to our model (based on user input) - // This is required because when we have a server error we actually invalidate - // the form which means it cannot be resubmitted. - // So once a field is changed that has a server error assigned to it - // we need to re-validate it for the server side validator so the user can resubmit - // the form. Of course normal client-side validators will continue to execute. - scope.$watch("property.value", function(newValue) { - //we are explicitly checking for valServer errors here, since we shouldn't auto clear - // based on other errors. We'll also check if there's no other validation errors apart from valPropertyMsg, if valPropertyMsg - // is the only one, then we'll clear. - - if (!newValue) { - return; - } - - var errCount = 0; - for (var e in formCtrl.$error) { - if (angular.isArray(formCtrl.$error[e])) { - errCount++; - } - } - - if ((errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) { - scope.errorMsg = ""; - formCtrl.$setValidity('valPropertyMsg', true); - } - }, true); - - //listen for server validation changes - // NOTE: we pass in "" in order to listen for all validation changes to the content property, not for - // validation changes to fields in the property this is because some server side validators may not - // return the field name for which the error belongs too, just the property for which it belongs. - // It's important to note that we need to subscribe to server validation changes here because we always must - // indicate that a content property is invalid at the property level since developers may not actually implement - // the correct field validation in their property editors. - - if (scope.property) { //this can be null if no property was assigned - serverValidationManager.subscribe(scope.property.alias, "", function(isValid, propertyErrors, allErrors) { - hasError = !isValid; - if (hasError) { - //set the error message to the server message - scope.errorMsg = propertyErrors[0].errorMsg; - //flag that the current validator is invalid - formCtrl.$setValidity('valPropertyMsg', false); - } - else { - scope.errorMsg = ""; - //flag that the current validator is valid - formCtrl.$setValidity('valPropertyMsg', true); - } - }); - - //when the element is disposed we need to unsubscribe! - // NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain - // but they are a different callback instance than the above. - element.bind('$destroy', function() { - serverValidationManager.unsubscribe(scope.property.alias, ""); - }); - } - } - }; -} +/** +* @ngdoc directive +* @name umbraco.directives.directive:valPropertyMsg +* @restrict A +* @element textarea +* @requires formController +* @description This directive is used to control the display of the property level validation message. +* We will listen for server side validation changes +* and when an error is detected for this property we'll show the error message. +* In order for this directive to work, the valStatusChanged directive must be placed on the containing form. +**/ +function valPropertyMsg(serverValidationManager) { + return { + scope: { + property: "=property" + }, + require: "^form", //require that this directive is contained within an ngForm + replace: true, //replace the element with the template + restrict: "E", //restrict to element + template: "
    {{errorMsg}}
    ", + + /** + Our directive requries a reference to a form controller + which gets passed in to this parameter + */ + link: function (scope, element, attrs, formCtrl) { + + //if there's any remaining errors in the server validation service then we should show them. + var showValidation = serverValidationManager.items.length > 0; + var hasError = false; + + //create properties on our custom scope so we can use it in our template + scope.errorMsg = ""; + + //listen for form error changes + scope.$on("valStatusChanged", function(evt, args) { + if (args.form.$invalid) { + + //first we need to check if the valPropertyMsg validity is invalid + if (formCtrl.$error.valPropertyMsg && formCtrl.$error.valPropertyMsg.length > 0) { + //since we already have an error we'll just return since this means we've already set the + // hasError and errorMsg properties which occurs below in the serverValidationManager.subscribe + return; + } + else if (element.closest(".umb-control-group").find(".ng-invalid").length > 0) { + //check if it's one of the properties that is invalid in the current content property + hasError = true; + //update the validation message if we don't already have one assigned. + if (showValidation && scope.errorMsg === "") { + var err; + //this can be null if no property was assigned + if (scope.property) { + err = serverValidationManager.getPropertyError(scope.property.alias, ""); + } + scope.errorMsg = err ? err.errorMsg : "Property has errors"; + } + } + else { + hasError = false; + scope.errorMsg = ""; + } + } + else { + hasError = false; + scope.errorMsg = ""; + } + }, true); + + //listen for the forms saving event + scope.$on("formSubmitting", function (ev, args) { + showValidation = true; + if (hasError && scope.errorMsg === "") { + var err; + //this can be null if no property was assigned + if (scope.property) { + err = serverValidationManager.getPropertyError(scope.property.alias, ""); + } + scope.errorMsg = err ? err.errorMsg : "Property has errors"; + } + else if (!hasError) { + scope.errorMsg = ""; + } + }); + + //listen for the forms saved event + scope.$on("formSubmitted", function (ev, args) { + showValidation = false; + scope.errorMsg = ""; + formCtrl.$setValidity('valPropertyMsg', true); + }); + + //We need to subscribe to any changes to our model (based on user input) + // This is required because when we have a server error we actually invalidate + // the form which means it cannot be resubmitted. + // So once a field is changed that has a server error assigned to it + // we need to re-validate it for the server side validator so the user can resubmit + // the form. Of course normal client-side validators will continue to execute. + scope.$watch("property.value", function(newValue) { + //we are explicitly checking for valServer errors here, since we shouldn't auto clear + // based on other errors. We'll also check if there's no other validation errors apart from valPropertyMsg, if valPropertyMsg + // is the only one, then we'll clear. + + if (!newValue) { + return; + } + + var errCount = 0; + for (var e in formCtrl.$error) { + if (angular.isArray(formCtrl.$error[e])) { + errCount++; + } + } + + if ((errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) { + scope.errorMsg = ""; + formCtrl.$setValidity('valPropertyMsg', true); + } + }, true); + + //listen for server validation changes + // NOTE: we pass in "" in order to listen for all validation changes to the content property, not for + // validation changes to fields in the property this is because some server side validators may not + // return the field name for which the error belongs too, just the property for which it belongs. + // It's important to note that we need to subscribe to server validation changes here because we always must + // indicate that a content property is invalid at the property level since developers may not actually implement + // the correct field validation in their property editors. + + if (scope.property) { //this can be null if no property was assigned + serverValidationManager.subscribe(scope.property.alias, "", function(isValid, propertyErrors, allErrors) { + hasError = !isValid; + if (hasError) { + //set the error message to the server message + scope.errorMsg = propertyErrors[0].errorMsg; + //flag that the current validator is invalid + formCtrl.$setValidity('valPropertyMsg', false); + } + else { + scope.errorMsg = ""; + //flag that the current validator is valid + formCtrl.$setValidity('valPropertyMsg', true); + } + }); + + //when the element is disposed we need to unsubscribe! + // NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain + // but they are a different callback instance than the above. + element.bind('$destroy', function() { + serverValidationManager.unsubscribe(scope.property.alias, ""); + }); + } + } + }; +} angular.module('umbraco.directives').directive("valPropertyMsg", valPropertyMsg); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valregex.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valregex.directive.js index d1103cdbc3..e98fb06c98 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valregex.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valregex.directive.js @@ -1,64 +1,64 @@ -/** - * @ngdoc directive - * @name umbraco.directives.directive:valRegex - * @restrict A - * @description A custom directive to allow for matching a value against a regex string. - * NOTE: there's already an ng-pattern but this requires that a regex expression is set, not a regex string - **/ -function valRegex() { - - return { - require: 'ngModel', - restrict: "A", - link: function (scope, elm, attrs, ctrl) { - - var flags = ""; - if (attrs.valRegexFlags) { - try { - flags = scope.$eval(attrs.valRegexFlags); - if (!flags) { - flags = attrs.valRegexFlags; - } - } - catch (e) { - flags = attrs.valRegexFlags; - } - } - var regex; - try { - var resolved = scope.$eval(attrs.valRegex); - if (resolved) { - regex = new RegExp(resolved, flags); - } - else { - regex = new RegExp(attrs.valRegex, flags); - } - } - catch(e) { - regex = new RegExp(attrs.valRegex, flags); - } - - var patternValidator = function (viewValue) { - //NOTE: we don't validate on empty values, use required validator for that - if (!viewValue || regex.test(viewValue)) { - // it is valid - ctrl.$setValidity('valRegex', true); - //assign a message to the validator - ctrl.errorMsg = ""; - return viewValue; - } - else { - // it is invalid, return undefined (no model update) - ctrl.$setValidity('valRegex', false); - //assign a message to the validator - ctrl.errorMsg = "Value is invalid, it does not match the correct pattern"; - return undefined; - } - }; - - ctrl.$formatters.push(patternValidator); - ctrl.$parsers.push(patternValidator); - } - }; -} +/** + * @ngdoc directive + * @name umbraco.directives.directive:valRegex + * @restrict A + * @description A custom directive to allow for matching a value against a regex string. + * NOTE: there's already an ng-pattern but this requires that a regex expression is set, not a regex string + **/ +function valRegex() { + + return { + require: 'ngModel', + restrict: "A", + link: function (scope, elm, attrs, ctrl) { + + var flags = ""; + if (attrs.valRegexFlags) { + try { + flags = scope.$eval(attrs.valRegexFlags); + if (!flags) { + flags = attrs.valRegexFlags; + } + } + catch (e) { + flags = attrs.valRegexFlags; + } + } + var regex; + try { + var resolved = scope.$eval(attrs.valRegex); + if (resolved) { + regex = new RegExp(resolved, flags); + } + else { + regex = new RegExp(attrs.valRegex, flags); + } + } + catch(e) { + regex = new RegExp(attrs.valRegex, flags); + } + + var patternValidator = function (viewValue) { + //NOTE: we don't validate on empty values, use required validator for that + if (!viewValue || regex.test(viewValue)) { + // it is valid + ctrl.$setValidity('valRegex', true); + //assign a message to the validator + ctrl.errorMsg = ""; + return viewValue; + } + else { + // it is invalid, return undefined (no model update) + ctrl.$setValidity('valRegex', false); + //assign a message to the validator + ctrl.errorMsg = "Value is invalid, it does not match the correct pattern"; + return undefined; + } + }; + + ctrl.$formatters.push(patternValidator); + ctrl.$parsers.push(patternValidator); + } + }; +} angular.module('umbraco.directives').directive("valRegex", valRegex); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserver.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserver.directive.js index 02d1677c95..fbc91dcb11 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserver.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserver.directive.js @@ -1,67 +1,67 @@ -/** - * @ngdoc directive - * @name umbraco.directives.directive:valServer - * @restrict A - * @description This directive is used to associate a content property with a server-side validation response - * so that the validators in angular are updated based on server-side feedback. - **/ -function valServer(serverValidationManager) { - return { - require: 'ngModel', - restrict: "A", - link: function (scope, element, attr, ctrl) { - - if (!scope.model || !scope.model.alias){ - throw "valServer can only be used in the scope of a content property object"; - } - var currentProperty = scope.model; - - //default to 'value' if nothing is set - var fieldName = "value"; - if (attr.valServer) { - fieldName = scope.$eval(attr.valServer); - if (!fieldName) { - //eval returned nothing so just use the string - fieldName = attr.valServer; - } - } - - //Need to watch the value model for it to change, previously we had subscribed to - //ctrl.$viewChangeListeners but this is not good enough if you have an editor that - // doesn't specifically have a 2 way ng binding. This is required because when we - // have a server error we actually invalidate the form which means it cannot be - // resubmitted. So once a field is changed that has a server error assigned to it - // we need to re-validate it for the server side validator so the user can resubmit - // the form. Of course normal client-side validators will continue to execute. - scope.$watch(function() { - return ctrl.$modelValue; - }, function (newValue) { - if (ctrl.$invalid) { - ctrl.$setValidity('valServer', true); - } - }); - - //subscribe to the server validation changes - serverValidationManager.subscribe(currentProperty.alias, fieldName, function (isValid, propertyErrors, allErrors) { - if (!isValid) { - ctrl.$setValidity('valServer', false); - //assign an error msg property to the current validator - ctrl.errorMsg = propertyErrors[0].errorMsg; - } - else { - ctrl.$setValidity('valServer', true); - //reset the error message - ctrl.errorMsg = ""; - } - }); - - //when the element is disposed we need to unsubscribe! - // NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain - // but they are a different callback instance than the above. - element.bind('$destroy', function () { - serverValidationManager.unsubscribe(currentProperty.alias, fieldName); - }); - } - }; -} +/** + * @ngdoc directive + * @name umbraco.directives.directive:valServer + * @restrict A + * @description This directive is used to associate a content property with a server-side validation response + * so that the validators in angular are updated based on server-side feedback. + **/ +function valServer(serverValidationManager) { + return { + require: 'ngModel', + restrict: "A", + link: function (scope, element, attr, ctrl) { + + if (!scope.model || !scope.model.alias){ + throw "valServer can only be used in the scope of a content property object"; + } + var currentProperty = scope.model; + + //default to 'value' if nothing is set + var fieldName = "value"; + if (attr.valServer) { + fieldName = scope.$eval(attr.valServer); + if (!fieldName) { + //eval returned nothing so just use the string + fieldName = attr.valServer; + } + } + + //Need to watch the value model for it to change, previously we had subscribed to + //ctrl.$viewChangeListeners but this is not good enough if you have an editor that + // doesn't specifically have a 2 way ng binding. This is required because when we + // have a server error we actually invalidate the form which means it cannot be + // resubmitted. So once a field is changed that has a server error assigned to it + // we need to re-validate it for the server side validator so the user can resubmit + // the form. Of course normal client-side validators will continue to execute. + scope.$watch(function() { + return ctrl.$modelValue; + }, function (newValue) { + if (ctrl.$invalid) { + ctrl.$setValidity('valServer', true); + } + }); + + //subscribe to the server validation changes + serverValidationManager.subscribe(currentProperty.alias, fieldName, function (isValid, propertyErrors, allErrors) { + if (!isValid) { + ctrl.$setValidity('valServer', false); + //assign an error msg property to the current validator + ctrl.errorMsg = propertyErrors[0].errorMsg; + } + else { + ctrl.$setValidity('valServer', true); + //reset the error message + ctrl.errorMsg = ""; + } + }); + + //when the element is disposed we need to unsubscribe! + // NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain + // but they are a different callback instance than the above. + element.bind('$destroy', function () { + serverValidationManager.unsubscribe(currentProperty.alias, fieldName); + }); + } + }; +} angular.module('umbraco.directives').directive("valServer", valServer); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserverfield.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserverfield.directive.js index 3e853f800a..da79253c2f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserverfield.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserverfield.directive.js @@ -1,54 +1,54 @@ -/** - * @ngdoc directive - * @name umbraco.directives.directive:valServerField - * @restrict A - * @description This directive is used to associate a content field (not user defined) with a server-side validation response - * so that the validators in angular are updated based on server-side feedback. - **/ -function valServerField(serverValidationManager) { - return { - require: 'ngModel', - restrict: "A", - link: function (scope, element, attr, ctrl) { - - if (!attr.valServerField) { - throw "valServerField must have a field name for referencing server errors"; - } - - var fieldName = attr.valServerField; - - //subscribe to the changed event of the view model. This is required because when we - // have a server error we actually invalidate the form which means it cannot be - // resubmitted. So once a field is changed that has a server error assigned to it - // we need to re-validate it for the server side validator so the user can resubmit - // the form. Of course normal client-side validators will continue to execute. - ctrl.$viewChangeListeners.push(function () { - if (ctrl.$invalid) { - ctrl.$setValidity('valServerField', true); - } - }); - - //subscribe to the server validation changes - serverValidationManager.subscribe(null, fieldName, function (isValid, fieldErrors, allErrors) { - if (!isValid) { - ctrl.$setValidity('valServerField', false); - //assign an error msg property to the current validator - ctrl.errorMsg = fieldErrors[0].errorMsg; - } - else { - ctrl.$setValidity('valServerField', true); - //reset the error message - ctrl.errorMsg = ""; - } - }); - - //when the element is disposed we need to unsubscribe! - // NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain - // but they are a different callback instance than the above. - element.bind('$destroy', function () { - serverValidationManager.unsubscribe(null, fieldName); - }); - } - }; -} +/** + * @ngdoc directive + * @name umbraco.directives.directive:valServerField + * @restrict A + * @description This directive is used to associate a content field (not user defined) with a server-side validation response + * so that the validators in angular are updated based on server-side feedback. + **/ +function valServerField(serverValidationManager) { + return { + require: 'ngModel', + restrict: "A", + link: function (scope, element, attr, ctrl) { + + if (!attr.valServerField) { + throw "valServerField must have a field name for referencing server errors"; + } + + var fieldName = attr.valServerField; + + //subscribe to the changed event of the view model. This is required because when we + // have a server error we actually invalidate the form which means it cannot be + // resubmitted. So once a field is changed that has a server error assigned to it + // we need to re-validate it for the server side validator so the user can resubmit + // the form. Of course normal client-side validators will continue to execute. + ctrl.$viewChangeListeners.push(function () { + if (ctrl.$invalid) { + ctrl.$setValidity('valServerField', true); + } + }); + + //subscribe to the server validation changes + serverValidationManager.subscribe(null, fieldName, function (isValid, fieldErrors, allErrors) { + if (!isValid) { + ctrl.$setValidity('valServerField', false); + //assign an error msg property to the current validator + ctrl.errorMsg = fieldErrors[0].errorMsg; + } + else { + ctrl.$setValidity('valServerField', true); + //reset the error message + ctrl.errorMsg = ""; + } + }); + + //when the element is disposed we need to unsubscribe! + // NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain + // but they are a different callback instance than the above. + element.bind('$destroy', function () { + serverValidationManager.unsubscribe(null, fieldName); + }); + } + }; +} angular.module('umbraco.directives').directive("valServerField", valServerField); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtab.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtab.directive.js index 30ad4f58e7..0216fe14d7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtab.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtab.directive.js @@ -1,38 +1,38 @@ - -/** -* @ngdoc directive -* @name umbraco.directives.directive:valTab -* @restrict A -* @description Used to show validation warnings for a tab to indicate that the tab content has validations errors in its data. -* In order for this directive to work, the valFormManager directive must be placed on the containing form. -**/ -function valTab() { - return { - require: "^form", - restrict: "A", - link: function (scope, element, attr, formCtrl) { - - var tabId = "tab" + scope.tab.id; - - scope.tabHasError = false; - - //listen for form validation changes - scope.$on("valStatusChanged", function(evt, args) { - if (!args.form.$valid) { - var tabContent = element.closest(".umb-panel").find("#" + tabId); - //check if the validation messages are contained inside of this tabs - if (tabContent.find(".ng-invalid").length > 0) { - scope.tabHasError = true; - } else { - scope.tabHasError = false; - } - } - else { - scope.tabHasError = false; - } - }); - - } - }; -} + +/** +* @ngdoc directive +* @name umbraco.directives.directive:valTab +* @restrict A +* @description Used to show validation warnings for a tab to indicate that the tab content has validations errors in its data. +* In order for this directive to work, the valFormManager directive must be placed on the containing form. +**/ +function valTab() { + return { + require: "^form", + restrict: "A", + link: function (scope, element, attr, formCtrl) { + + var tabId = "tab" + scope.tab.id; + + scope.tabHasError = false; + + //listen for form validation changes + scope.$on("valStatusChanged", function(evt, args) { + if (!args.form.$valid) { + var tabContent = element.closest(".umb-panel").find("#" + tabId); + //check if the validation messages are contained inside of this tabs + if (tabContent.find(".ng-invalid").length > 0) { + scope.tabHasError = true; + } else { + scope.tabHasError = false; + } + } + else { + scope.tabHasError = false; + } + }); + + } + }; +} angular.module('umbraco.directives').directive("valTab", valTab); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtogglemsg.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtogglemsg.directive.js index e5ec11bf5f..5a57754524 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtogglemsg.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtogglemsg.directive.js @@ -1,87 +1,87 @@ -function valToggleMsg(serverValidationManager) { - return { - require: "^form", - restrict: "A", - - /** - Our directive requries a reference to a form controller which gets passed in to this parameter - */ - link: function (scope, element, attr, formCtrl) { - - if (!attr.valToggleMsg){ - throw "valToggleMsg requires that a reference to a validator is specified"; - } - if (!attr.valMsgFor){ - throw "valToggleMsg requires that the attribute valMsgFor exists on the element"; - } - if (!formCtrl[attr.valMsgFor]) { - throw "valToggleMsg cannot find field " + attr.valMsgFor + " on form " + formCtrl.$name; - } - - //if there's any remaining errors in the server validation service then we should show them. - var showValidation = serverValidationManager.items.length > 0; - var hasCustomMsg = element.contents().length > 0; - - //add a watch to the validator for the value (i.e. myForm.value.$error.required ) - scope.$watch(function () { - //sometimes if a dialog closes in the middle of digest we can get null references here - - return (formCtrl && formCtrl[attr.valMsgFor]) ? formCtrl[attr.valMsgFor].$error[attr.valToggleMsg] : null; - }, function () { - //sometimes if a dialog closes in the middle of digest we can get null references here - if ((formCtrl && formCtrl[attr.valMsgFor])) { - if (formCtrl[attr.valMsgFor].$error[attr.valToggleMsg] && showValidation) { - element.show(); - //display the error message if this element has no contents - if (!hasCustomMsg) { - element.html(formCtrl[attr.valMsgFor].errorMsg); - } - } - else { - element.hide(); - } - } - }); - - //listen for the saving event (the result is a callback method which is called to unsubscribe) - var unsubscribeSaving = scope.$on("formSubmitting", function (ev, args) { - showValidation = true; - if (formCtrl[attr.valMsgFor].$error[attr.valToggleMsg]) { - element.show(); - //display the error message if this element has no contents - if (!hasCustomMsg) { - element.html(formCtrl[attr.valMsgFor].errorMsg); - } - } - else { - element.hide(); - } - }); - - //listen for the saved event (the result is a callback method which is called to unsubscribe) - var unsubscribeSaved = scope.$on("formSubmitted", function (ev, args) { - showValidation = false; - element.hide(); - }); - - //when the element is disposed we need to unsubscribe! - // NOTE: this is very important otherwise if this directive is part of a modal, the listener still exists because the dom - // element might still be there even after the modal has been hidden. - element.bind('$destroy', function () { - unsubscribeSaving(); - unsubscribeSaved(); - }); - - } - }; -} - -/** -* @ngdoc directive -* @name umbraco.directives.directive:valToggleMsg -* @restrict A -* @element input -* @requires formController -* @description This directive will show/hide an error based on: is the value + the given validator invalid? AND, has the form been submitted ? -**/ +function valToggleMsg(serverValidationManager) { + return { + require: "^form", + restrict: "A", + + /** + Our directive requries a reference to a form controller which gets passed in to this parameter + */ + link: function (scope, element, attr, formCtrl) { + + if (!attr.valToggleMsg){ + throw "valToggleMsg requires that a reference to a validator is specified"; + } + if (!attr.valMsgFor){ + throw "valToggleMsg requires that the attribute valMsgFor exists on the element"; + } + if (!formCtrl[attr.valMsgFor]) { + throw "valToggleMsg cannot find field " + attr.valMsgFor + " on form " + formCtrl.$name; + } + + //if there's any remaining errors in the server validation service then we should show them. + var showValidation = serverValidationManager.items.length > 0; + var hasCustomMsg = element.contents().length > 0; + + //add a watch to the validator for the value (i.e. myForm.value.$error.required ) + scope.$watch(function () { + //sometimes if a dialog closes in the middle of digest we can get null references here + + return (formCtrl && formCtrl[attr.valMsgFor]) ? formCtrl[attr.valMsgFor].$error[attr.valToggleMsg] : null; + }, function () { + //sometimes if a dialog closes in the middle of digest we can get null references here + if ((formCtrl && formCtrl[attr.valMsgFor])) { + if (formCtrl[attr.valMsgFor].$error[attr.valToggleMsg] && showValidation) { + element.show(); + //display the error message if this element has no contents + if (!hasCustomMsg) { + element.html(formCtrl[attr.valMsgFor].errorMsg); + } + } + else { + element.hide(); + } + } + }); + + //listen for the saving event (the result is a callback method which is called to unsubscribe) + var unsubscribeSaving = scope.$on("formSubmitting", function (ev, args) { + showValidation = true; + if (formCtrl[attr.valMsgFor].$error[attr.valToggleMsg]) { + element.show(); + //display the error message if this element has no contents + if (!hasCustomMsg) { + element.html(formCtrl[attr.valMsgFor].errorMsg); + } + } + else { + element.hide(); + } + }); + + //listen for the saved event (the result is a callback method which is called to unsubscribe) + var unsubscribeSaved = scope.$on("formSubmitted", function (ev, args) { + showValidation = false; + element.hide(); + }); + + //when the element is disposed we need to unsubscribe! + // NOTE: this is very important otherwise if this directive is part of a modal, the listener still exists because the dom + // element might still be there even after the modal has been hidden. + element.bind('$destroy', function () { + unsubscribeSaving(); + unsubscribeSaved(); + }); + + } + }; +} + +/** +* @ngdoc directive +* @name umbraco.directives.directive:valToggleMsg +* @restrict A +* @element input +* @requires formController +* @description This directive will show/hide an error based on: is the value + the given validator invalid? AND, has the form been submitted ? +**/ angular.module('umbraco.directives').directive("valToggleMsg", valToggleMsg); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/editors/prevalues.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/editors/prevalues.mocks.js index 3e5b102887..a0af9fb1d2 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/editors/prevalues.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/editors/prevalues.mocks.js @@ -1,21 +1,21 @@ -angular.module('umbraco.mocks'). - factory('prevaluesMocks', ['$httpBackend', 'mocksUtils', function ($httpBackend, mocksUtils) { - 'use strict'; - - function getRichTextConfiguration(status, data, headers) { - if (!mocksUtils.checkAuth()) { - return [401, null, null]; - } - else { - return [200, { "plugins": [{ "name": "code", "useOnFrontend": true }, { "name": "paste", "useOnFrontend": true }, { "name": "umbracolink", "useOnFrontend": true }], "commands": [{ "icon": "images/editor/code.gif", "command": "code", "alias": "code", "userInterface": "false", "frontEndCommand": "code", "value": "", "priority": 1, "isStylePicker": false }, { "icon": "images/editor/removeformat.gif", "command": "removeformat", "alias": "removeformat", "userInterface": "false", "frontEndCommand": "removeformat", "value": "", "priority": 2, "isStylePicker": false }, { "icon": "images/editor/undo.gif", "command": "undo", "alias": "undo", "userInterface": "false", "frontEndCommand": "undo", "value": "", "priority": 11, "isStylePicker": false }, { "icon": "images/editor/redo.gif", "command": "redo", "alias": "redo", "userInterface": "false", "frontEndCommand": "redo", "value": "", "priority": 12, "isStylePicker": false }, { "icon": "images/editor/cut.gif", "command": "cut", "alias": "cut", "userInterface": "false", "frontEndCommand": "cut", "value": "", "priority": 13, "isStylePicker": false }, { "icon": "images/editor/copy.gif", "command": "copy", "alias": "copy", "userInterface": "false", "frontEndCommand": "copy", "value": "", "priority": 14, "isStylePicker": false }, { "icon": "images/editor/showStyles.png", "command": "styleselect", "alias": "styleselect", "userInterface": "false", "frontEndCommand": "styleselect", "value": "", "priority": 20, "isStylePicker": false }, { "icon": "images/editor/bold.gif", "command": "bold", "alias": "bold", "userInterface": "false", "frontEndCommand": "bold", "value": "", "priority": 21, "isStylePicker": false }, { "icon": "images/editor/italic.gif", "command": "italic", "alias": "italic", "userInterface": "false", "frontEndCommand": "italic", "value": "", "priority": 22, "isStylePicker": false }, { "icon": "images/editor/underline.gif", "command": "underline", "alias": "underline", "userInterface": "false", "frontEndCommand": "underline", "value": "", "priority": 23, "isStylePicker": false }, { "icon": "images/editor/strikethrough.gif", "command": "strikethrough", "alias": "strikethrough", "userInterface": "false", "frontEndCommand": "strikethrough", "value": "", "priority": 24, "isStylePicker": false }, { "icon": "images/editor/justifyleft.gif", "command": "justifyleft", "alias": "justifyleft", "userInterface": "false", "frontEndCommand": "alignleft", "value": "", "priority": 31, "isStylePicker": false }, { "icon": "images/editor/justifycenter.gif", "command": "justifycenter", "alias": "justifycenter", "userInterface": "false", "frontEndCommand": "aligncenter", "value": "", "priority": 32, "isStylePicker": false }, { "icon": "images/editor/justifyright.gif", "command": "justifyright", "alias": "justifyright", "userInterface": "false", "frontEndCommand": "alignright", "value": "", "priority": 33, "isStylePicker": false }, { "icon": "images/editor/justifyfull.gif", "command": "justifyfull", "alias": "justifyfull", "userInterface": "false", "frontEndCommand": "alignfull", "value": "", "priority": 34, "isStylePicker": false }, { "icon": "images/editor/bullist.gif", "command": "bullist", "alias": "bullist", "userInterface": "false", "frontEndCommand": "bullist", "value": "", "priority": 41, "isStylePicker": false }, { "icon": "images/editor/numlist.gif", "command": "numlist", "alias": "numlist", "userInterface": "false", "frontEndCommand": "numlist", "value": "", "priority": 42, "isStylePicker": false }, { "icon": "images/editor/outdent.gif", "command": "outdent", "alias": "outdent", "userInterface": "false", "frontEndCommand": "outdent", "value": "", "priority": 43, "isStylePicker": false }, { "icon": "images/editor/indent.gif", "command": "indent", "alias": "indent", "userInterface": "false", "frontEndCommand": "indent", "value": "", "priority": 44, "isStylePicker": false }, { "icon": "images/editor/link.gif", "command": "link", "alias": "mcelink", "userInterface": "true", "frontEndCommand": "link", "value": "", "priority": 51, "isStylePicker": false }, { "icon": "images/editor/unLink.gif", "command": "unlink", "alias": "unlink", "userInterface": "false", "frontEndCommand": "unlink", "value": "", "priority": 52, "isStylePicker": false }, { "icon": "images/editor/anchor.gif", "command": "anchor", "alias": "mceinsertanchor", "userInterface": "false", "frontEndCommand": "anchor", "value": "", "priority": 53, "isStylePicker": false }, { "icon": "images/editor/image.gif", "command": "image", "alias": "mceimage", "userInterface": "true", "frontEndCommand": "umbmediapicker", "value": "", "priority": 61, "isStylePicker": false }, { "icon": "images/editor/insMacro.gif", "command": "umbracomacro", "alias": "umbracomacro", "userInterface": "true", "frontEndCommand": "umbmacro", "value": "", "priority": 62, "isStylePicker": false }, { "icon": "images/editor/table.gif", "command": "table", "alias": "mceinserttable", "userInterface": "true", "frontEndCommand": "table", "value": "", "priority": 63, "isStylePicker": false }, { "icon": "images/editor/media.gif", "command": "umbracoembed", "alias": "umbracoembed", "userInterface": "true", "frontEndCommand": "umbembeddialog", "value": "", "priority": 66, "isStylePicker": false }, { "icon": "images/editor/hr.gif", "command": "hr", "alias": "inserthorizontalrule", "userInterface": "false", "frontEndCommand": "hr", "value": "", "priority": 71, "isStylePicker": false }, { "icon": "images/editor/sub.gif", "command": "sub", "alias": "subscript", "userInterface": "false", "frontEndCommand": "sub", "value": "", "priority": 72, "isStylePicker": false }, { "icon": "images/editor/sup.gif", "command": "sup", "alias": "superscript", "userInterface": "false", "frontEndCommand": "sup", "value": "", "priority": 73, "isStylePicker": false }, { "icon": "images/editor/charmap.gif", "command": "charmap", "alias": "mcecharmap", "userInterface": "false", "frontEndCommand": "charmap", "value": "", "priority": 74, "isStylePicker": false }], "validElements": "+a[id|style|rel|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel],-sub[style|class],-sup[style|class],-blockquote[dir|style|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align|style],address[class|align|style],-h1[id|dir|class|align],-h2[id|dir|class|align],-h3[id|dir|class|align],-h4[id|dir|class|align],-h5[id|dir|class|align],-h6[id|style|dir|class|align],hr[class|style],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*]", "inValidElements": "font", "customConfig": { "entity_encoding": "raw", "spellchecker_rpc_url": "GoogleSpellChecker.ashx" } }, null]; - } - } - - return { - register: function() { - $httpBackend - .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/RichTextPreValue/GetConfiguration')) - .respond(getRichTextConfiguration); - } - }; +angular.module('umbraco.mocks'). + factory('prevaluesMocks', ['$httpBackend', 'mocksUtils', function ($httpBackend, mocksUtils) { + 'use strict'; + + function getRichTextConfiguration(status, data, headers) { + if (!mocksUtils.checkAuth()) { + return [401, null, null]; + } + else { + return [200, { "plugins": [{ "name": "code", "useOnFrontend": true }, { "name": "paste", "useOnFrontend": true }, { "name": "umbracolink", "useOnFrontend": true }], "commands": [{ "icon": "images/editor/code.gif", "command": "code", "alias": "code", "userInterface": "false", "frontEndCommand": "code", "value": "", "priority": 1, "isStylePicker": false }, { "icon": "images/editor/removeformat.gif", "command": "removeformat", "alias": "removeformat", "userInterface": "false", "frontEndCommand": "removeformat", "value": "", "priority": 2, "isStylePicker": false }, { "icon": "images/editor/undo.gif", "command": "undo", "alias": "undo", "userInterface": "false", "frontEndCommand": "undo", "value": "", "priority": 11, "isStylePicker": false }, { "icon": "images/editor/redo.gif", "command": "redo", "alias": "redo", "userInterface": "false", "frontEndCommand": "redo", "value": "", "priority": 12, "isStylePicker": false }, { "icon": "images/editor/cut.gif", "command": "cut", "alias": "cut", "userInterface": "false", "frontEndCommand": "cut", "value": "", "priority": 13, "isStylePicker": false }, { "icon": "images/editor/copy.gif", "command": "copy", "alias": "copy", "userInterface": "false", "frontEndCommand": "copy", "value": "", "priority": 14, "isStylePicker": false }, { "icon": "images/editor/showStyles.png", "command": "styleselect", "alias": "styleselect", "userInterface": "false", "frontEndCommand": "styleselect", "value": "", "priority": 20, "isStylePicker": false }, { "icon": "images/editor/bold.gif", "command": "bold", "alias": "bold", "userInterface": "false", "frontEndCommand": "bold", "value": "", "priority": 21, "isStylePicker": false }, { "icon": "images/editor/italic.gif", "command": "italic", "alias": "italic", "userInterface": "false", "frontEndCommand": "italic", "value": "", "priority": 22, "isStylePicker": false }, { "icon": "images/editor/underline.gif", "command": "underline", "alias": "underline", "userInterface": "false", "frontEndCommand": "underline", "value": "", "priority": 23, "isStylePicker": false }, { "icon": "images/editor/strikethrough.gif", "command": "strikethrough", "alias": "strikethrough", "userInterface": "false", "frontEndCommand": "strikethrough", "value": "", "priority": 24, "isStylePicker": false }, { "icon": "images/editor/justifyleft.gif", "command": "justifyleft", "alias": "justifyleft", "userInterface": "false", "frontEndCommand": "alignleft", "value": "", "priority": 31, "isStylePicker": false }, { "icon": "images/editor/justifycenter.gif", "command": "justifycenter", "alias": "justifycenter", "userInterface": "false", "frontEndCommand": "aligncenter", "value": "", "priority": 32, "isStylePicker": false }, { "icon": "images/editor/justifyright.gif", "command": "justifyright", "alias": "justifyright", "userInterface": "false", "frontEndCommand": "alignright", "value": "", "priority": 33, "isStylePicker": false }, { "icon": "images/editor/justifyfull.gif", "command": "justifyfull", "alias": "justifyfull", "userInterface": "false", "frontEndCommand": "alignfull", "value": "", "priority": 34, "isStylePicker": false }, { "icon": "images/editor/bullist.gif", "command": "bullist", "alias": "bullist", "userInterface": "false", "frontEndCommand": "bullist", "value": "", "priority": 41, "isStylePicker": false }, { "icon": "images/editor/numlist.gif", "command": "numlist", "alias": "numlist", "userInterface": "false", "frontEndCommand": "numlist", "value": "", "priority": 42, "isStylePicker": false }, { "icon": "images/editor/outdent.gif", "command": "outdent", "alias": "outdent", "userInterface": "false", "frontEndCommand": "outdent", "value": "", "priority": 43, "isStylePicker": false }, { "icon": "images/editor/indent.gif", "command": "indent", "alias": "indent", "userInterface": "false", "frontEndCommand": "indent", "value": "", "priority": 44, "isStylePicker": false }, { "icon": "images/editor/link.gif", "command": "link", "alias": "mcelink", "userInterface": "true", "frontEndCommand": "link", "value": "", "priority": 51, "isStylePicker": false }, { "icon": "images/editor/unLink.gif", "command": "unlink", "alias": "unlink", "userInterface": "false", "frontEndCommand": "unlink", "value": "", "priority": 52, "isStylePicker": false }, { "icon": "images/editor/anchor.gif", "command": "anchor", "alias": "mceinsertanchor", "userInterface": "false", "frontEndCommand": "anchor", "value": "", "priority": 53, "isStylePicker": false }, { "icon": "images/editor/image.gif", "command": "image", "alias": "mceimage", "userInterface": "true", "frontEndCommand": "umbmediapicker", "value": "", "priority": 61, "isStylePicker": false }, { "icon": "images/editor/insMacro.gif", "command": "umbracomacro", "alias": "umbracomacro", "userInterface": "true", "frontEndCommand": "umbmacro", "value": "", "priority": 62, "isStylePicker": false }, { "icon": "images/editor/table.gif", "command": "table", "alias": "mceinserttable", "userInterface": "true", "frontEndCommand": "table", "value": "", "priority": 63, "isStylePicker": false }, { "icon": "images/editor/media.gif", "command": "umbracoembed", "alias": "umbracoembed", "userInterface": "true", "frontEndCommand": "umbembeddialog", "value": "", "priority": 66, "isStylePicker": false }, { "icon": "images/editor/hr.gif", "command": "hr", "alias": "inserthorizontalrule", "userInterface": "false", "frontEndCommand": "hr", "value": "", "priority": 71, "isStylePicker": false }, { "icon": "images/editor/sub.gif", "command": "sub", "alias": "subscript", "userInterface": "false", "frontEndCommand": "sub", "value": "", "priority": 72, "isStylePicker": false }, { "icon": "images/editor/sup.gif", "command": "sup", "alias": "superscript", "userInterface": "false", "frontEndCommand": "sup", "value": "", "priority": 73, "isStylePicker": false }, { "icon": "images/editor/charmap.gif", "command": "charmap", "alias": "mcecharmap", "userInterface": "false", "frontEndCommand": "charmap", "value": "", "priority": 74, "isStylePicker": false }], "validElements": "+a[id|style|rel|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel],-sub[style|class],-sup[style|class],-blockquote[dir|style|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align|style],address[class|align|style],-h1[id|dir|class|align],-h2[id|dir|class|align],-h3[id|dir|class|align],-h4[id|dir|class|align],-h5[id|dir|class|align],-h6[id|style|dir|class|align],hr[class|style],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*]", "inValidElements": "font", "customConfig": { "entity_encoding": "raw", "spellchecker_rpc_url": "GoogleSpellChecker.ashx" } }, null]; + } + } + + return { + register: function() { + $httpBackend + .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/RichTextPreValue/GetConfiguration')) + .respond(getRichTextConfiguration); + } + }; }]); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js index e0efcc39ab..523f9c51f2 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js @@ -1,297 +1,297 @@ -angular.module('umbraco.mocks'). - factory('mocksUtils', ['$cookieStore', function($cookieStore) { - 'use strict'; - - //by default we will perform authorization - var doAuth = true; - - return { - - getMockDataType: function(id, selectedId) { - var dataType = { - id: id, - name: "Simple editor " + id, - selectedEditor: selectedId, - availableEditors: [ - { name: "Simple editor 1", editorId: String.CreateGuid() }, - { name: "Simple editor 2", editorId: String.CreateGuid() }, - { name: "Simple editor " + id, editorId: selectedId }, - { name: "Simple editor 4", editorId: String.CreateGuid() }, - { name: "Simple editor 5", editorId: String.CreateGuid() }, - { name: "Simple editor 6", editorId: String.CreateGuid() } - ], - preValues: [ - { - label: "Custom pre value 1 for editor " + selectedId, - description: "Enter a value for this pre-value", - key: "myPreVal1", - view: "requiredfield" - }, - { - label: "Custom pre value 2 for editor " + selectedId, - description: "Enter a value for this pre-value", - key: "myPreVal2", - view: "requiredfield" - } - ] - - }; - return dataType; - }, - - /** Creats a mock content object */ - getMockContent: function(id) { - var node = { - name: "My content with id: " + id, - updateDate: new Date().toIsoDateTimeString(), - publishDate: new Date().toIsoDateTimeString(), - createDate: new Date().toIsoDateTimeString(), - id: id, - parentId: 1234, - icon: "icon-umb-content", - owner: { name: "Administrator", id: 0 }, - updater: { name: "Per Ploug Krogslund", id: 1 }, - path: "-1,1234,2455", - allowedActions: ["U", "H", "A"], - tabs: [ - { - label: "Child documents", - id: 1, - active: true, - properties: [ - { alias: "list", label: "List", view: "listview", value: "", hideLabel: true }, - { alias: "media", label: "Media picker", view: "mediapicker", value: "" } - ] - }, - { - label: "Content", - id: 2, - properties: [ - { alias: "valTest", label: "Validation test", view: "validationtest", value: "asdfasdf" }, - { alias: "bodyText", label: "Body Text", description: "Here you enter the primary article contents", view: "rte", value: "

    askjdkasj lasjd

    ", config: {} }, - { alias: "textarea", label: "textarea", view: "textarea", value: "ajsdka sdjkds", config: { rows: 4 } }, - { alias: "map", label: "Map", view: "googlemaps", value: "37.4419,-122.1419", config: { mapType: "ROADMAP", zoom: 4 } }, - - { alias: "content", label: "Content picker", view: "contentpicker", value: "1234,23242,23232,23231" } - ] - }, - { - label: "Sample Editor", - id: 3, - properties: [ - { alias: "datepicker", label: "Datepicker", view: "datepicker", config: { pickTime: false, format: "yyyy-MM-dd" } }, - { alias: "tags", label: "Tags", view: "tags", value: "" } - ] - }, - { - label: "This", - id: 4, - properties: [ - { alias: "valTest4", label: "Validation test", view: "validationtest", value: "asdfasdf" }, - { alias: "bodyText4", label: "Body Text", description: "Here you enter the primary article contents", view: "rte", value: "

    askjdkasj lasjd

    ", config: {} }, - { alias: "textarea4", label: "textarea", view: "textarea", value: "ajsdka sdjkds", config: { rows: 4 } }, - { alias: "map4", label: "Map", view: "googlemaps", value: "37.4419,-122.1419", config: { mapType: "ROADMAP", zoom: 4 } }, - - { alias: "content4", label: "Content picker", view: "contentpicker", value: "1234,23242,23232,23231" } - ] - }, - { - label: "Is", - id: 5, - properties: [ - { alias: "valTest5", label: "Validation test", view: "validationtest", value: "asdfasdf" }, - { alias: "bodyText5", label: "Body Text", description: "Here you enter the primary article contents", view: "rte", value: "

    askjdkasj lasjd

    ", config: {} }, - { alias: "textarea5", label: "textarea", view: "textarea", value: "ajsdka sdjkds", config: { rows: 4 } }, - { alias: "map5", label: "Map", view: "googlemaps", value: "37.4419,-122.1419", config: { mapType: "ROADMAP", zoom: 4 } }, - - { alias: "content5", label: "Content picker", view: "contentpicker", value: "1234,23242,23232,23231" } - ] - }, - { - label: "Overflown", - id: 6, - properties: [ - { alias: "valTest6", label: "Validation test", view: "validationtest", value: "asdfasdf" }, - { alias: "bodyText6", label: "Body Text", description: "Here you enter the primary article contents", view: "rte", value: "

    askjdkasj lasjd

    ", config: {} }, - { alias: "textarea6", label: "textarea", view: "textarea", value: "ajsdka sdjkds", config: { rows: 4 } }, - { alias: "map6", label: "Map", view: "googlemaps", value: "37.4419,-122.1419", config: { mapType: "ROADMAP", zoom: 4 } }, - - { alias: "content6", label: "Content picker", view: "contentpicker", value: "1234,23242,23232,23231" } - ] - }, - { - label: "Tab # 7", - id: 7, - properties: [ - { alias: "valTest7", label: "Validation test", view: "validationtest", value: "asdfasdf" }, - { alias: "bodyText7", label: "Body Text", description: "Here you enter the primary article contents", view: "rte", value: "

    askjdkasj lasjd

    ", config: {} }, - { alias: "textarea7", label: "textarea", view: "textarea", value: "ajsdka sdjkds", config: { rows: 4 } }, - { alias: "map7", label: "Map", view: "googlemaps", value: "37.4419,-122.1419", config: { mapType: "ROADMAP", zoom: 4 } }, - - { alias: "content7", label: "Content picker", view: "contentpicker", value: "1234,23242,23232,23231" } - ] - }, - { - label: "Grid", - id: 8, - properties: [ - { alias: "grid", label: "Grid", view: "grid", value: "test", hideLabel: true } - ] - }, { - label: "Generic Properties", - id: 0, - properties: [ - { - label: 'Id', - value: 1234, - view: "readonlyvalue", - alias: "_umb_id" - }, - { - label: 'Created by', - description: 'Original author', - value: "Administrator", - view: "readonlyvalue", - alias: "_umb_createdby" - }, - { - label: 'Created', - description: 'Date/time this document was created', - value: new Date().toIsoDateTimeString(), - view: "readonlyvalue", - alias: "_umb_createdate" - }, - { - label: 'Updated', - description: 'Date/time this document was created', - value: new Date().toIsoDateTimeString(), - view: "readonlyvalue", - alias: "_umb_updatedate" - }, - { - label: 'Document Type', - value: "Home page", - view: "readonlyvalue", - alias: "_umb_doctype" - }, - { - label: 'Publish at', - description: 'Date/time to publish this document', - value: new Date().toIsoDateTimeString(), - view: "datepicker", - alias: "_umb_releasedate" - }, - { - label: 'Unpublish at', - description: 'Date/time to un-publish this document', - value: new Date().toIsoDateTimeString(), - view: "datepicker", - alias: "_umb_expiredate" - }, - { - label: 'Template', - value: "myTemplate", - view: "dropdown", - alias: "_umb_template", - config: { - items: { - "" : "-- Choose template --", - "myTemplate" : "My Templates", - "home" : "Home Page", - "news" : "News Page" - } - } - }, - { - label: 'Link to document', - value: ["/testing" + id, "http://localhost/testing" + id, "http://mydomain.com/testing" + id].join(), - view: "urllist", - alias: "_umb_urllist" - }, - { - alias: "test", label: "Stuff", view: "test", value: "", - config: { - fields: [ - { alias: "embedded", label: "Embbeded", view: "textstring", value: "" }, - { alias: "embedded2", label: "Embbeded 2", view: "contentpicker", value: "" }, - { alias: "embedded3", label: "Embbeded 3", view: "textarea", value: "" }, - { alias: "embedded4", label: "Embbeded 4", view: "datepicker", value: "" } - ] - } - } - ] - } - ] - }; - - return node; - }, - - getMockEntity : function(id){ - return {name: "hello", id: id, icon: "icon-file"}; - }, - - /** generally used for unit tests, calling this will disable the auth check and always return true */ - disableAuth: function() { - doAuth = false; - }, - - /** generally used for unit tests, calling this will enabled the auth check */ - enabledAuth: function() { - doAuth = true; - }, - - /** Checks for our mock auth cookie, if it's not there, returns false */ - checkAuth: function () { - if (doAuth) { - var mockAuthCookie = $cookieStore.get("mockAuthCookie"); - if (!mockAuthCookie) { - return false; - } - return true; - } - else { - return true; - } - }, - - /** Creates/sets the auth cookie with a value indicating the user is now authenticated */ - setAuth: function() { - //set the cookie for loging - $cookieStore.put("mockAuthCookie", "Logged in!"); - }, - - /** removes the auth cookie */ - clearAuth: function() { - $cookieStore.remove("mockAuthCookie"); - }, - - urlRegex: function(url) { - url = url.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); - return new RegExp("^" + url); - }, - - getParameterByName: function(url, name) { - name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); - - var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), - results = regex.exec(url); - - return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); - }, - - getParametersByName: function(url, name) { - name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); - - var regex = new RegExp(name + "=([^&#]*)", "mg"), results = []; - var match; - - while ( ( match = regex.exec(url) ) !== null ) - { - results.push(decodeURIComponent(match[1].replace(/\+/g, " "))); - } - - return results; - } - }; - }]); +angular.module('umbraco.mocks'). + factory('mocksUtils', ['$cookieStore', function($cookieStore) { + 'use strict'; + + //by default we will perform authorization + var doAuth = true; + + return { + + getMockDataType: function(id, selectedId) { + var dataType = { + id: id, + name: "Simple editor " + id, + selectedEditor: selectedId, + availableEditors: [ + { name: "Simple editor 1", editorId: String.CreateGuid() }, + { name: "Simple editor 2", editorId: String.CreateGuid() }, + { name: "Simple editor " + id, editorId: selectedId }, + { name: "Simple editor 4", editorId: String.CreateGuid() }, + { name: "Simple editor 5", editorId: String.CreateGuid() }, + { name: "Simple editor 6", editorId: String.CreateGuid() } + ], + preValues: [ + { + label: "Custom pre value 1 for editor " + selectedId, + description: "Enter a value for this pre-value", + key: "myPreVal1", + view: "requiredfield" + }, + { + label: "Custom pre value 2 for editor " + selectedId, + description: "Enter a value for this pre-value", + key: "myPreVal2", + view: "requiredfield" + } + ] + + }; + return dataType; + }, + + /** Creats a mock content object */ + getMockContent: function(id) { + var node = { + name: "My content with id: " + id, + updateDate: new Date().toIsoDateTimeString(), + publishDate: new Date().toIsoDateTimeString(), + createDate: new Date().toIsoDateTimeString(), + id: id, + parentId: 1234, + icon: "icon-umb-content", + owner: { name: "Administrator", id: 0 }, + updater: { name: "Per Ploug Krogslund", id: 1 }, + path: "-1,1234,2455", + allowedActions: ["U", "H", "A"], + tabs: [ + { + label: "Child documents", + id: 1, + active: true, + properties: [ + { alias: "list", label: "List", view: "listview", value: "", hideLabel: true }, + { alias: "media", label: "Media picker", view: "mediapicker", value: "" } + ] + }, + { + label: "Content", + id: 2, + properties: [ + { alias: "valTest", label: "Validation test", view: "validationtest", value: "asdfasdf" }, + { alias: "bodyText", label: "Body Text", description: "Here you enter the primary article contents", view: "rte", value: "

    askjdkasj lasjd

    ", config: {} }, + { alias: "textarea", label: "textarea", view: "textarea", value: "ajsdka sdjkds", config: { rows: 4 } }, + { alias: "map", label: "Map", view: "googlemaps", value: "37.4419,-122.1419", config: { mapType: "ROADMAP", zoom: 4 } }, + + { alias: "content", label: "Content picker", view: "contentpicker", value: "1234,23242,23232,23231" } + ] + }, + { + label: "Sample Editor", + id: 3, + properties: [ + { alias: "datepicker", label: "Datepicker", view: "datepicker", config: { pickTime: false, format: "yyyy-MM-dd" } }, + { alias: "tags", label: "Tags", view: "tags", value: "" } + ] + }, + { + label: "This", + id: 4, + properties: [ + { alias: "valTest4", label: "Validation test", view: "validationtest", value: "asdfasdf" }, + { alias: "bodyText4", label: "Body Text", description: "Here you enter the primary article contents", view: "rte", value: "

    askjdkasj lasjd

    ", config: {} }, + { alias: "textarea4", label: "textarea", view: "textarea", value: "ajsdka sdjkds", config: { rows: 4 } }, + { alias: "map4", label: "Map", view: "googlemaps", value: "37.4419,-122.1419", config: { mapType: "ROADMAP", zoom: 4 } }, + + { alias: "content4", label: "Content picker", view: "contentpicker", value: "1234,23242,23232,23231" } + ] + }, + { + label: "Is", + id: 5, + properties: [ + { alias: "valTest5", label: "Validation test", view: "validationtest", value: "asdfasdf" }, + { alias: "bodyText5", label: "Body Text", description: "Here you enter the primary article contents", view: "rte", value: "

    askjdkasj lasjd

    ", config: {} }, + { alias: "textarea5", label: "textarea", view: "textarea", value: "ajsdka sdjkds", config: { rows: 4 } }, + { alias: "map5", label: "Map", view: "googlemaps", value: "37.4419,-122.1419", config: { mapType: "ROADMAP", zoom: 4 } }, + + { alias: "content5", label: "Content picker", view: "contentpicker", value: "1234,23242,23232,23231" } + ] + }, + { + label: "Overflown", + id: 6, + properties: [ + { alias: "valTest6", label: "Validation test", view: "validationtest", value: "asdfasdf" }, + { alias: "bodyText6", label: "Body Text", description: "Here you enter the primary article contents", view: "rte", value: "

    askjdkasj lasjd

    ", config: {} }, + { alias: "textarea6", label: "textarea", view: "textarea", value: "ajsdka sdjkds", config: { rows: 4 } }, + { alias: "map6", label: "Map", view: "googlemaps", value: "37.4419,-122.1419", config: { mapType: "ROADMAP", zoom: 4 } }, + + { alias: "content6", label: "Content picker", view: "contentpicker", value: "1234,23242,23232,23231" } + ] + }, + { + label: "Tab # 7", + id: 7, + properties: [ + { alias: "valTest7", label: "Validation test", view: "validationtest", value: "asdfasdf" }, + { alias: "bodyText7", label: "Body Text", description: "Here you enter the primary article contents", view: "rte", value: "

    askjdkasj lasjd

    ", config: {} }, + { alias: "textarea7", label: "textarea", view: "textarea", value: "ajsdka sdjkds", config: { rows: 4 } }, + { alias: "map7", label: "Map", view: "googlemaps", value: "37.4419,-122.1419", config: { mapType: "ROADMAP", zoom: 4 } }, + + { alias: "content7", label: "Content picker", view: "contentpicker", value: "1234,23242,23232,23231" } + ] + }, + { + label: "Grid", + id: 8, + properties: [ + { alias: "grid", label: "Grid", view: "grid", value: "test", hideLabel: true } + ] + }, { + label: "Generic Properties", + id: 0, + properties: [ + { + label: 'Id', + value: 1234, + view: "readonlyvalue", + alias: "_umb_id" + }, + { + label: 'Created by', + description: 'Original author', + value: "Administrator", + view: "readonlyvalue", + alias: "_umb_createdby" + }, + { + label: 'Created', + description: 'Date/time this document was created', + value: new Date().toIsoDateTimeString(), + view: "readonlyvalue", + alias: "_umb_createdate" + }, + { + label: 'Updated', + description: 'Date/time this document was created', + value: new Date().toIsoDateTimeString(), + view: "readonlyvalue", + alias: "_umb_updatedate" + }, + { + label: 'Document Type', + value: "Home page", + view: "readonlyvalue", + alias: "_umb_doctype" + }, + { + label: 'Publish at', + description: 'Date/time to publish this document', + value: new Date().toIsoDateTimeString(), + view: "datepicker", + alias: "_umb_releasedate" + }, + { + label: 'Unpublish at', + description: 'Date/time to un-publish this document', + value: new Date().toIsoDateTimeString(), + view: "datepicker", + alias: "_umb_expiredate" + }, + { + label: 'Template', + value: "myTemplate", + view: "dropdown", + alias: "_umb_template", + config: { + items: { + "" : "-- Choose template --", + "myTemplate" : "My Templates", + "home" : "Home Page", + "news" : "News Page" + } + } + }, + { + label: 'Link to document', + value: ["/testing" + id, "http://localhost/testing" + id, "http://mydomain.com/testing" + id].join(), + view: "urllist", + alias: "_umb_urllist" + }, + { + alias: "test", label: "Stuff", view: "test", value: "", + config: { + fields: [ + { alias: "embedded", label: "Embbeded", view: "textstring", value: "" }, + { alias: "embedded2", label: "Embbeded 2", view: "contentpicker", value: "" }, + { alias: "embedded3", label: "Embbeded 3", view: "textarea", value: "" }, + { alias: "embedded4", label: "Embbeded 4", view: "datepicker", value: "" } + ] + } + } + ] + } + ] + }; + + return node; + }, + + getMockEntity : function(id){ + return {name: "hello", id: id, icon: "icon-file"}; + }, + + /** generally used for unit tests, calling this will disable the auth check and always return true */ + disableAuth: function() { + doAuth = false; + }, + + /** generally used for unit tests, calling this will enabled the auth check */ + enabledAuth: function() { + doAuth = true; + }, + + /** Checks for our mock auth cookie, if it's not there, returns false */ + checkAuth: function () { + if (doAuth) { + var mockAuthCookie = $cookieStore.get("mockAuthCookie"); + if (!mockAuthCookie) { + return false; + } + return true; + } + else { + return true; + } + }, + + /** Creates/sets the auth cookie with a value indicating the user is now authenticated */ + setAuth: function() { + //set the cookie for loging + $cookieStore.put("mockAuthCookie", "Logged in!"); + }, + + /** removes the auth cookie */ + clearAuth: function() { + $cookieStore.remove("mockAuthCookie"); + }, + + urlRegex: function(url) { + url = url.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + return new RegExp("^" + url); + }, + + getParameterByName: function(url, name) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + + var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), + results = regex.exec(url); + + return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); + }, + + getParametersByName: function(url, name) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + + var regex = new RegExp(name + "=([^&#]*)", "mg"), results = []; + var match; + + while ( ( match = regex.exec(url) ) !== null ) + { + results.push(decodeURIComponent(match[1].replace(/\+/g, " "))); + } + + return results; + } + }; + }]); diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/dashboard.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/dashboard.mocks.js index 7582db6740..d51cd2d6c4 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/dashboard.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/dashboard.mocks.js @@ -1,24 +1,24 @@ -angular.module('umbraco.mocks'). - factory('dashboardMocks', ['$httpBackend', 'mocksUtils', function ($httpBackend, mocksUtils) { - 'use strict'; - - function getDashboard(status, data, headers) { - //check for existence of a cookie so we can do login/logout in the belle app (ignore for tests). - if (!mocksUtils.checkAuth()) { - return [401, null, null]; - } - else { - //TODO: return real mocked data - return [200, [], null]; - } - } - - return { - register: function() { - - $httpBackend - .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Dashboard/GetDashboard')) - .respond(getDashboard); - } - }; +angular.module('umbraco.mocks'). + factory('dashboardMocks', ['$httpBackend', 'mocksUtils', function ($httpBackend, mocksUtils) { + 'use strict'; + + function getDashboard(status, data, headers) { + //check for existence of a cookie so we can do login/logout in the belle app (ignore for tests). + if (!mocksUtils.checkAuth()) { + return [401, null, null]; + } + else { + //TODO: return real mocked data + return [200, [], null]; + } + } + + return { + register: function() { + + $httpBackend + .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Dashboard/GetDashboard')) + .respond(getDashboard); + } + }; }]); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/macro.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/macro.mocks.js index 435bdd3486..7ec5f670d8 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/macro.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/macro.mocks.js @@ -1,32 +1,32 @@ -angular.module('umbraco.mocks'). - factory('macroMocks', ['$httpBackend', 'mocksUtils', function ($httpBackend, mocksUtils) { - 'use strict'; - - function returnParameters(status, data, headers) { - - if (!mocksUtils.checkAuth()) { - return [401, null, null]; - } - - var nodes = [{ - alias: "parameter1", - name: "Parameter 1" - }, { - alias: "parameter2", - name: "Parameter 2" - }]; - - return [200, nodes, null]; - } - - - return { - register: function () { - - $httpBackend - .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Macro/GetMacroParameters')) - .respond(returnParameters); - - } - }; +angular.module('umbraco.mocks'). + factory('macroMocks', ['$httpBackend', 'mocksUtils', function ($httpBackend, mocksUtils) { + 'use strict'; + + function returnParameters(status, data, headers) { + + if (!mocksUtils.checkAuth()) { + return [401, null, null]; + } + + var nodes = [{ + alias: "parameter1", + name: "Parameter 1" + }, { + alias: "parameter2", + name: "Parameter 2" + }]; + + return [200, nodes, null]; + } + + + return { + register: function () { + + $httpBackend + .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Macro/GetMacroParameters')) + .respond(returnParameters); + + } + }; }]); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js index adb28ce045..2d1bd0914e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js @@ -1,766 +1,766 @@ -angular.module('umbraco.mocks'). - factory('localizationMocks', ['$httpBackend', 'mocksUtils', function ($httpBackend, mocksUtils) { - 'use strict'; - - function getLanguageResource(status, data, headers) { - //check for existence of a cookie so we can do login/logout in the belle app (ignore for tests). - if (!mocksUtils.checkAuth()) { - return [401, null, null]; - } - else { - return [200, { - "actions_assignDomain": "Culture and Hostnames", - "actions_auditTrail": "Audit Trail", - "actions_browse": "Browse Node", - "actions_changeDocType": "Change Document Type", - "actions_copy": "Copy", - "actions_create": "Create", - "actions_createPackage": "Create Package", - "actions_delete": "Delete", - "actions_disable": "Disable", - "actions_emptyTrashcan": "Empty recycle bin", - "actions_exportDocumentType": "Export Document Type", - "actions_importDocumentType": "Import Document Type", - "actions_importPackage": "Import Package", - "actions_liveEdit": "Edit in Canvas", - "actions_logout": "Exit", - "actions_move": "Move", - "actions_notify": "Notifications", - "actions_protect": "Public access", - "actions_publish": "Publish", - "actions_unpublish": "Unpublish", - "actions_refreshNode": "Reload nodes", - "actions_republish": "Republish entire site", - "actions_rights": "Permissions", - "actions_rollback": "Rollback", - "actions_sendtopublish": "Send To Publish", - "actions_sendToTranslate": "Send To Translation", - "actions_sort": "Sort", - "actions_toPublish": "Send to publication", - "actions_translate": "Translate", - "actions_update": "Update", - "actions_exportContourForm": "Export form", - "actions_importContourForm": "Import form", - "actions_archiveContourForm": "Archive form", - "actions_unarchiveContourForm": "Unarchive form", - "actions_defaultValue": "Default value", - "assignDomain_permissionDenied": "Permission denied.", - "assignDomain_addNew": "Add new Domain", - "assignDomain_remove": "remove", - "assignDomain_invalidNode": "Invalid node.", - "assignDomain_invalidDomain": "Invalid domain format.", - "assignDomain_duplicateDomain": "Domain has already been assigned.", - "assignDomain_domain": "Domain", - "assignDomain_language": "Language", - "assignDomain_domainCreated": "New domain '%0%' has been created", - "assignDomain_domainDeleted": "Domain '%0%' is deleted", - "assignDomain_domainExists": "Domain '%0%' has already been assigned", - "assignDomain_domainHelp": "Valid domain names are: 'example.com', 'www.example.com', 'example.com:8080' or 'https://www.example.com/'.

    One-level paths in domains are supported, eg. 'example.com/en'. However, they should be avoided. Better use the culture setting above.", - "assignDomain_domainUpdated": "Domain '%0%' has been updated", - "assignDomain_orEdit": "Edit Current Domains", - "assignDomain_inherit": "Inherit", - "assignDomain_setLanguage": "Culture", - "assignDomain_setLanguageHelp": "Set the culture for nodes below the current node,
    or inherit culture from parent nodes. Will also apply
    to the current node, unless a domain below applies too.", - "assignDomain_setDomains": "Domains", - "auditTrails_atViewingFor": "Viewing for", - "buttons_select": "Select", - "buttons_somethingElse": "Do something else", - "buttons_bold": "Bold", - "buttons_deindent": "Cancel Paragraph Indent", - "buttons_formFieldInsert": "Insert form field", - "buttons_graphicHeadline": "Insert graphic headline", - "buttons_htmlEdit": "Edit Html", - "buttons_indent": "Indent Paragraph", - "buttons_italic": "Italic", - "buttons_justifyCenter": "Center", - "buttons_justifyLeft": "Justify Left", - "buttons_justifyRight": "Justify Right", - "buttons_linkInsert": "Insert Link", - "buttons_linkLocal": "Insert local link (anchor)", - "buttons_listBullet": "Bullet List", - "buttons_listNumeric": "Numeric List", - "buttons_macroInsert": "Insert macro", - "buttons_pictureInsert": "Insert picture", - "buttons_relations": "Edit relations", - "buttons_save": "Save", - "buttons_saveAndPublish": "Save and publish", - "buttons_saveToPublish": "Save and send for approval", - "buttons_showPage": "Preview", - "buttons_showPageDisabled": "Preview is disabled because there's no template assigned", - "buttons_styleChoose": "Choose style", - "buttons_styleShow": "Show styles", - "buttons_tableInsert": "Insert table", - "changeDocType_changeDocTypeInstruction": "To change the document type for the selected content, first select from the list of valid types for this location.", - "changeDocType_changeDocTypeInstruction2": "Then confirm and/or amend the mapping of properties from the current type to the new, and click Save.", - "changeDocType_contentRepublished": "The content has been re-published.", - "changeDocType_currentProperty": "Current Property", - "changeDocType_currentType": "Current type", - "changeDocType_docTypeCannotBeChanged": "The document type cannot be changed, as there are no alternatives valid for this location.", - "changeDocType_docTypeChanged": "Document Type Changed", - "changeDocType_mapProperties": "Map Properties", - "changeDocType_mapToProperty": "Map to Property", - "changeDocType_newTemplate": "New Template", - "changeDocType_newType": "New Type", - "changeDocType_none": "none", - "changeDocType_selectedContent": "Content", - "changeDocType_selectNewDocType": "Select New Document Type", - "changeDocType_successMessage": "The document type of the selected content has been successfully changed to [new type] and the following properties mapped:", - "changeDocType_to": "to", - "changeDocType_validationErrorPropertyWithMoreThanOneMapping": "Could not complete property mapping as one or more properties have more than one mapping defined.", - "changeDocType_validDocTypesNote": "Only alternate types valid for the current location are displayed.", - "content_about": "About this page", - "content_alias": "Alias", - "content_alternativeTextHelp": "(how would you describe the picture over the phone)", - "content_alternativeUrls": "Alternative Links", - "content_clickToEdit": "Click to edit this item", - "content_createBy": "Created by", - "content_createByDesc": "Original autho", - "content_updatedBy": "Updated by", - "content_createDate": "Created", - "content_createDateDesc": "Date/time this document was created", - "content_documentType": "Document Type", - "content_editing": "Editing", - "content_expireDate": "Remove at", - "content_itemChanged": "This item has been changed after publication", - "content_itemNotPublished": "This item is not published", - "content_lastPublished": "Last published", - "content_mediatype": "Media Type", - "content_mediaLinks": "Link to media item(s)", - "content_membergroup": "Member Group", - "content_memberrole": "Role", - "content_membertype": "Member Type", - "content_noDate": "No date chosen", - "content_nodeName": "Page Title", - "content_otherElements": "Properties", - "content_parentNotPublished": "This document is published but is not visible because the parent '%0%' is unpublished", - "content_parentNotPublishedAnomaly": "Oops: this document is published but is not in the cache (internal error)", - "content_publish": "Publish", - "content_publishStatus": "Publication Status", - "content_releaseDate": "Publish at", - "content_removeDate": "Clear Date", - "content_sortDone": "Sortorder is updated", - "content_sortHelp": "To sort the nodes, simply drag the nodes or click one of the column headers. You can select multiple nodes by holding the 'shift' or 'control' key while selecting", - "content_statistics": "Statistics", - "content_titleOptional": "Title (optional)", - "content_type": "Type", - "content_unPublish": "Unpublish", - "content_updateDate": "Last edited", - "content_updateDateDesc": "Date/time this document was created", - "content_uploadClear": "Remove file", - "content_urls": "Link to document", - "content_memberof": "Member of group(s)", - "content_notmemberof": "Not a member of group(s)", - "content_childItems": "Child items", - "create_chooseNode": "Where do you want to create the new %0%", - "create_createUnder": "Create a page under", - "create_updateData": "Choose a type and a title", - "create_noDocumentTypes": "There are no allowed document types available. You must enable these in the settings section under 'document types'.", - "create_noMediaTypes": "There are no allowed media types available. You must enable these in the settings section under 'media types'.", - "dashboard_browser": "Browse your website", - "dashboard_dontShowAgain": "- Hide", - "dashboard_nothinghappens": "If umbraco isn't opening, you might need to allow popups from this site", - "dashboard_openinnew": "has opened in a new window", - "dashboard_restart": "Restart", - "dashboard_visit": "Visit", - "dashboard_welcome": "Welcome", - "defaultdialogs_anchorInsert": "Name", - "defaultdialogs_assignDomain": "Manage hostnames", - "defaultdialogs_closeThisWindow": "Close this window", - "defaultdialogs_confirmdelete": "Are you sure you want to delete", - "defaultdialogs_confirmdisable": "Are you sure you want to disable", - "defaultdialogs_confirmEmptyTrashcan": "Please check this box to confirm deletion of %0% item(s)", - "defaultdialogs_confirmlogout": "Are you sure?", - "defaultdialogs_confirmSure": "Are you sure?", - "defaultdialogs_cut": "Cut", - "defaultdialogs_editdictionary": "Edit Dictionary Item", - "defaultdialogs_editlanguage": "Edit Language", - "defaultdialogs_insertAnchor": "Insert local link", - "defaultdialogs_insertCharacter": "Insert character", - "defaultdialogs_insertgraphicheadline": "Insert graphic headline", - "defaultdialogs_insertimage": "Insert picture", - "defaultdialogs_insertlink": "Insert link", - "defaultdialogs_insertMacro": "Click to add a Macro", - "defaultdialogs_inserttable": "Insert table", - "defaultdialogs_lastEdited": "Last Edited", - "defaultdialogs_link": "Link", - "defaultdialogs_linkinternal": "Internal link:", - "defaultdialogs_linklocaltip": "When using local links, insert '#' infront of link", - "defaultdialogs_linknewwindow": "Open in new window?", - "defaultdialogs_macroContainerSettings": "Macro Settings", - "defaultdialogs_macroDoesNotHaveProperties": "This macro does not contain any properties you can edit", - "defaultdialogs_paste": "Paste", - "defaultdialogs_permissionsEdit": "Edit Permissions for", - "defaultdialogs_recycleBinDeleting": "The items in the recycle bin are now being deleted. Please do not close this window while this operation takes place", - "defaultdialogs_recycleBinIsEmpty": "The recycle bin is now empty", - "defaultdialogs_recycleBinWarning": "When items are deleted from the recycle bin, they will be gone forever", - "defaultdialogs_regexSearchError": "regexlib.com's webservice is currently experiencing some problems, which we have no control over. We are very sorry for this inconvenience.", - "defaultdialogs_regexSearchHelp": "Search for a regular expression to add validation to a form field. Exemple: 'email, 'zip-code' 'url'", - "defaultdialogs_removeMacro": "Remove Macro", - "defaultdialogs_requiredField": "Required Field", - "defaultdialogs_sitereindexed": "Site is reindexed", - "defaultdialogs_siterepublished": "The website cache has been refreshed. All publish content is now uptodate. While all unpublished content is still unpublished", - "defaultdialogs_siterepublishHelp": "The website cache will be refreshed. All published content will be updated, while unpublished content will stay unpublished.", - "defaultdialogs_tableColumns": "Number of columns", - "defaultdialogs_tableRows": "Number of rows", - "defaultdialogs_templateContentAreaHelp": "Set a placeholder id by setting an ID on your placeholder you can inject content into this template from child templates, by refering this ID using a <asp:content /> element.", - "defaultdialogs_templateContentPlaceHolderHelp": "Select a placeholder id from the list below. You can only choose Id's from the current template's master.", - "defaultdialogs_thumbnailimageclickfororiginal": "Click on the image to see full size", - "defaultdialogs_treepicker": "Pick item", - "defaultdialogs_viewCacheItem": "View Cache Item", - "dictionaryItem_description": " Edit the different language versions for the dictionary item '%0%' below
    You can add additional languages under the 'languages' in the menu on the left ", - "dictionaryItem_displayName": "Culture Name", - "placeholders_username": "Enter your username", - "placeholders_password": "Enter your password", - "placeholders_entername": "Enter a name...", - "placeholders_nameentity": "Name the %0%...", - "placeholders_search": "Type to search...", - "placeholders_filter": "Type to filter...", - "editcontenttype_allowedchildnodetypes": "Allowed child nodetypes", - "editcontenttype_create": "Create", - "editcontenttype_deletetab": "Delete tab", - "editcontenttype_description": "Description", - "editcontenttype_newtab": "New tab", - "editcontenttype_tab": "Tab", - "editcontenttype_thumbnail": "Thumbnail", - "editcontenttype_iscontainercontenttype": "Use as container content type", - "editdatatype_addPrevalue": "Add prevalue", - "editdatatype_dataBaseDatatype": "Database datatype", - "editdatatype_guid": "Property editor GUID", - "editdatatype_renderControl": "Property editor", - "editdatatype_rteButtons": "Buttons", - "editdatatype_rteEnableAdvancedSettings": "Enable advanced settings for", - "editdatatype_rteEnableContextMenu": "Enable context menu", - "editdatatype_rteMaximumDefaultImgSize": "Maximum default size of inserted images", - "editdatatype_rteRelatedStylesheets": "Related stylesheets", - "editdatatype_rteShowLabel": "Show label", - "editdatatype_rteWidthAndHeight": "Width and height", - "errorHandling_errorButDataWasSaved": "Your data has been saved, but before you can publish this page there are some errors you need to fix first:", - "errorHandling_errorChangingProviderPassword": "The current MemberShip Provider does not support changing password (EnablePasswordRetrieval need to be true)", - "errorHandling_errorExistsWithoutTab": "%0% already exists", - "errorHandling_errorHeader": "There were errors:", - "errorHandling_errorHeaderWithoutTab": "There were errors:", - "errorHandling_errorInPasswordFormat": "The password should be a minimum of %0% characters long and contain at least %1% non-alpha numeric character(s)", - "errorHandling_errorIntegerWithoutTab": "%0% must be an integer", - "errorHandling_errorMandatory": "The %0% field in the %1% tab is mandatory", - "errorHandling_errorMandatoryWithoutTab": "%0% is a mandatory field", - "errorHandling_errorRegExp": "%0% at %1% is not in a correct format", - "errorHandling_errorRegExpWithoutTab": "%0% is not in a correct format", - "errors_dissallowedMediaType": "The specified file type has been dissallowed by the administrator", - "errors_codemirroriewarning": "NOTE! Even though CodeMirror is enabled by configuration, it is disabled in Internet Explorer because it's not stable enough.", - "errors_contentTypeAliasAndNameNotNull": "Please fill both alias and name on the new propertytype!", - "errors_filePermissionsError": "There is a problem with read/write access to a specific file or folder", - "errors_missingTitle": "Please enter a title", - "errors_missingType": "Please choose a type", - "errors_pictureResizeBiggerThanOrg": "You're about to make the picture larger than the original size. Are you sure that you want to proceed?", - "errors_pythonErrorHeader": "Error in python script", - "errors_pythonErrorText": "The python script has not been saved, because it contained error(s)", - "errors_startNodeDoesNotExists": "Startnode deleted, please contact your administrator", - "errors_stylesMustMarkBeforeSelect": "Please mark content before changing style", - "errors_stylesNoStylesOnPage": "No active styles available", - "errors_tableColMergeLeft": "Please place cursor at the left of the two cells you wish to merge", - "errors_tableSplitNotSplittable": "You cannot split a cell that hasn't been merged.", - "errors_xsltErrorHeader": "Error in XSLT source", - "errors_xsltErrorText": "The XSLT has not been saved, because it contained error(s)", - "general_about": "About", - "general_action": "Action", - "general_add": "Add", - "general_alias": "Alias", - "general_areyousure": "Are you sure?", - "general_border": "Border", - "general_by": "or", - "general_cancel": "Cancel", - "general_cellMargin": "Cell margin", - "general_choose": "Choose", - "general_close": "Close", - "general_closewindow": "Close Window", - "general_comment": "Comment", - "general_confirm": "Confirm", - "general_constrainProportions": "Constrain proportions", - "general_continue": "Continue", - "general_copy": "Copy", - "general_create": "Create", - "general_database": "Database", - "general_date": "Date", - "general_default": "Default", - "general_delete": "Delete", - "general_deleted": "Deleted", - "general_deleting": "Deleting...", - "general_design": "Design", - "general_dimensions": "Dimensions", - "general_down": "Down", - "general_download": "Download", - "general_edit": "Edit", - "general_edited": "Edited", - "general_elements": "Elements", - "general_email": "Email", - "general_error": "Error", - "general_findDocument": "Find", - "general_height": "Height", - "general_help": "Help", - "general_icon": "Icon", - "general_import": "Import", - "general_innerMargin": "Inner margin", - "general_insert": "Insert", - "general_install": "Install", - "general_justify": "Justify", - "general_language": "Language", - "general_layout": "Layout", - "general_loading": "Loading", - "general_locked": "Locked", - "general_login": "Login", - "general_logoff": "Log off", - "general_logout": "Logout", - "general_macro": "Macro", - "general_move": "Move", - "general_name": "Name", - "general_new": "New", - "general_next": "Next", - "general_no": "No", - "general_of": "of", - "general_ok": "OK", - "general_open": "Open", - "general_or": "or", - "general_password": "Password", - "general_path": "Path", - "general_placeHolderID": "Placeholder ID", - "general_pleasewait": "One moment please...", - "general_previous": "Previous", - "general_properties": "Properties", - "general_reciept": "Email to receive form data", - "general_recycleBin": "Recycle Bin", - "general_remaining": "Remaining", - "general_rename": "Rename", - "general_renew": "Renew", - "general_required": "Required", - "general_retry": "Retry", - "general_rights": "Permissions", - "general_search": "Search", - "general_server": "Server", - "general_show": "Show", - "general_showPageOnSend": "Show page on Send", - "general_size": "Size", - "general_sort": "Sort", - "general_type": "Type", - "general_typeToSearch": "Type to search...", - "general_up": "Up", - "general_update": "Update", - "general_upgrade": "Upgrade", - "general_upload": "Upload", - "general_url": "Url", - "general_user": "User", - "general_username": "Username", - "general_value": "Value", - "general_view": "View", - "general_welcome": "Welcome...", - "general_width": "Width", - "general_yes": "Yes", - "general_folder": "Folder", - "general_searchResults": "Search results", - "graphicheadline_backgroundcolor": "Background color", - "graphicheadline_bold": "Bold", - "graphicheadline_color": "Text color", - "graphicheadline_font": "Font", - "graphicheadline_text": "Text", - "headers_page": "Page", - "installer_databaseErrorCannotConnect": "The installer cannot connect to the database.", - "installer_databaseErrorWebConfig": "Could not save the web.config file. Please modify the connection string manually.", - "installer_databaseFound": "Your database has been found and is identified as", - "installer_databaseHeader": "Database configuration", - "installer_databaseInstall": " Press the install button to install the Umbraco %0% database ", - "installer_databaseInstallDone": "Umbraco %0% has now been copied to your database. Press Next to proceed.", - "installer_databaseNotFound": "

    Database not found! Please check that the information in the 'connection string' of the \"web.config\" file is correct.

    To proceed, please edit the 'web.config' file (using Visual Studio or your favourite text editor), scroll to the bottom, add the connection string for your database in the key named 'umbracoDbDSN' and save the file.

    Click the retry button when done.
    More information on editing web.config here.

    ", - "installer_databaseText": "To complete this step, you must know some information regarding your database server ('connection string').
    Please contact your ISP if necessary. If you're installing on a local machine or server you might need information from your system administrator.", - "installer_databaseUpgrade": "

    Press the upgrade button to upgrade your database to Umbraco %0%

    Don't worry - no content will be deleted and everything will continue working afterwards!

    ", - "installer_databaseUpgradeDone": "Your database has been upgraded to the final version %0%.
    Press Next to proceed. ", - "installer_databaseUpToDate": "Your current database is up-to-date!. Click next to continue the configuration wizard", - "installer_defaultUserChangePass": "The Default users' password needs to be changed!", - "installer_defaultUserDisabled": "The Default user has been disabled or has no access to umbraco!

    No further actions needs to be taken. Click Next to proceed.", - "installer_defaultUserPassChanged": "The Default user's password has been successfully changed since the installation!

    No further actions needs to be taken. Click Next to proceed.", - "installer_defaultUserPasswordChanged": "The password is changed!", - "installer_defaultUserText": "

    umbraco creates a default user with a login ('admin') and password ('default'). It's important that the password is changed to something unique.

    This step will check the default user's password and suggest if it needs to be changed.

    ", - "installer_greatStart": "Get a great start, watch our introduction videos", - "installer_licenseText": "By clicking the next button (or modifying the umbracoConfigurationStatus in web.config), you accept the license for this software as specified in the box below. Notice that this umbraco distribution consists of two different licenses, the open source MIT license for the framework and the umbraco freeware license that covers the UI.", - "installer_None": "Not installed yet.", - "installer_permissionsAffectedFolders": "Affected files and folders", - "installer_permissionsAffectedFoldersMoreInfo": "More information on setting up permissions for umbraco here", - "installer_permissionsAffectedFoldersText": "You need to grant ASP.NET modify permissions to the following files/folders", - "installer_permissionsAlmostPerfect": "Your permission settings are almost perfect!

    You can run umbraco without problems, but you will not be able to install packages which are recommended to take full advantage of umbraco.", - "installer_permissionsHowtoResolve": "How to Resolve", - "installer_permissionsHowtoResolveLink": "Click here to read the text version", - "installer_permissionsHowtoResolveText": "Watch our video tutorial on setting up folder permissions for umbraco or read the text version.", - "installer_permissionsMaybeAnIssue": "Your permission settings might be an issue!

    You can run umbraco without problems, but you will not be able to create folders or install packages which are recommended to take full advantage of umbraco.", - "installer_permissionsNotReady": "Your permission settings are not ready for umbraco!

    In order to run umbraco, you'll need to update your permission settings.", - "installer_permissionsPerfect": "Your permission settings are perfect!

    You are ready to run umbraco and install packages!", - "installer_permissionsResolveFolderIssues": "Resolving folder issue", - "installer_permissionsResolveFolderIssuesLink": "Follow this link for more information on problems with ASP.NET and creating folders", - "installer_permissionsSettingUpPermissions": "Setting up folder permissions", - "installer_permissionsText": " umbraco needs write/modify access to certain directories in order to store files like pictures and PDF's. It also stores temporary data (aka: cache) for enhancing the performance of your website. ", - "installer_runwayFromScratch": "I want to start from scratch", - "installer_runwayFromScratchText": " Your website is completely empty at the moment, so that's perfect if you want to start from scratch and create your own document types and templates. (learn how) You can still choose to install Runway later on. Please go to the Developer section and choose Packages. ", - "installer_runwayHeader": "You've just set up a clean Umbraco platform. What do you want to do next?", - "installer_runwayInstalled": "Runway is installed", - "installer_runwayInstalledText": " You have the foundation in place. Select what modules you wish to install on top of it.
    This is our list of recommended modules, check off the ones you would like to install, or view the full list of modules ", - "installer_runwayOnlyProUsers": "Only recommended for experienced users", - "installer_runwaySimpleSite": "I want to start with a simple website", - "installer_runwaySimpleSiteText": "

    'Runway' is a simple website providing some basic document types and templates. The installer can set up Runway for you automatically, but you can easily edit, extend or remove it. It's not necessary and you can perfectly use Umbraco without it. However, Runway offers an easy foundation based on best practices to get you started faster than ever. If you choose to install Runway, you can optionally select basic building blocks called Runway Modules to enhance your Runway pages.

    Included with Runway: Home page, Getting Started page, Installing Modules page.
    Optional Modules: Top Navigation, Sitemap, Contact, Gallery.
    ", - "installer_runwayWhatIsRunway": "What is Runway", - "installer_step1": "Step 1/5 Accept license", - "installer_step2": "Step 2/5: Database configuration", - "installer_step3": "Step 3/5: Validating File Permissions", - "installer_step4": "Step 4/5: Check umbraco security", - "installer_step5": "Step 5/5: Umbraco is ready to get you started", - "installer_thankYou": "Thank you for choosing umbraco", - "installer_theEndBrowseSite": "

    Browse your new site

    You installed Runway, so why not see how your new website looks.", - "installer_theEndFurtherHelp": "

    Further help and information

    Get help from our award winning community, browse the documentation or watch some free videos on how to build a simple site, how to use packages and a quick guide to the umbraco terminology", - "installer_theEndHeader": "Umbraco %0% is installed and ready for use", - "installer_theEndInstallFailed": "To finish the installation, you'll need to manually edit the /web.config file and update the AppSetting key umbracoConfigurationStatus in the bottom to the value of '%0%'.", - "installer_theEndInstallSuccess": "You can get started instantly by clicking the 'Launch Umbraco' button below.
    If you are new to umbraco, you can find plenty of resources on our getting started pages.", - "installer_theEndOpenUmbraco": "

    Launch Umbraco

    To manage your website, simply open the umbraco back office and start adding content, updating the templates and stylesheets or add new functionality", - "installer_Unavailable": "Connection to database failed.", - "installer_Version3": "Umbraco Version 3", - "installer_Version4": "Umbraco Version 4", - "installer_watch": "Watch", - "installer_welcomeIntro": "This wizard will guide you through the process of configuring umbraco %0% for a fresh install or upgrading from version 3.0.

    Press 'next' to start the wizard.", - "language_cultureCode": "Culture Code", - "language_displayName": "Culture Name", - "lockout_lockoutWillOccur": "You've been idle and logout will automatically occur in", - "lockout_renewSession": "Renew now to save your work", - "login_greeting1": "Happy super sunday", - "login_greeting2": "Happy manic monday ", - "login_greeting3": "Happy tremendous tuesday", - "login_greeting4": "Happy wonderfull wednesday", - "login_greeting5": "Happy thunder thursday", - "login_greeting6": "Happy friendly friday", - "login_greeting7": "Happy shiny saturday", - "login_instruction": "log in below:", - "login_bottomText": "

    © 2001 - %0%
    umbraco.org

    ", - "main_dashboard": "Dashboard", - "main_sections": "Sections", - "main_tree": "Content", - "moveOrCopy_choose": "Choose page above...", - "moveOrCopy_copyDone": "%0% has been copied to %1%", - "moveOrCopy_copyTo": "Select where the document %0% should be copied to below", - "moveOrCopy_moveDone": "%0% has been moved to %1%", - "moveOrCopy_moveTo": "Select where the document %0% should be moved to below", - "moveOrCopy_nodeSelected": "has been selected as the root of your new content, click 'ok' below.", - "moveOrCopy_noNodeSelected": "No node selected yet, please select a node in the list above before clicking 'ok'", - "moveOrCopy_notAllowedByContentType": "The current node is not allowed under the chosen node because of its type", - "moveOrCopy_notAllowedByPath": "The current node cannot be moved to one of its subpages", - "moveOrCopy_notAllowedAtRoot": "The current node cannot exist at the root", - "moveOrCopy_notValid": "The action isn't allowed since you have insufficient permissions on 1 or more child documents.", - "moveOrCopy_relateToOriginal": "Relate copied items to original", - "notifications_editNotifications": "Edit your notification for %0%", - "notifications_mailBody": " Hi %0% This is an automated mail to inform you that the task '%1%' has been performed on the page '%2%' by the user '%3%' Go to http://%4%/actions/editContent.aspx?id=%5% to edit. Have a nice day! Cheers from the umbraco robot ", - "notifications_mailBodyHtml": "

    Hi %0%

    This is an automated mail to inform you that the task '%1%' has been performed on the page '%2%' by the user '%3%'

    Update summary:

    %6%

    Have a nice day!

    Cheers from the umbraco robot

    ", - "notifications_mailSubject": "[%0%] Notification about %1% performed on %2%", - "notifications_notifications": "Notifications", - "packager_chooseLocalPackageText": " Choose Package from your machine, by clicking the Browse
    button and locating the package. umbraco packages usually have a '.umb' or '.zip' extension. ", - "packager_packageAuthor": "Author", - "packager_packageDemonstration": "Demonstration", - "packager_packageDocumentation": "Documentation", - "packager_packageMetaData": "Package meta data", - "packager_packageName": "Package name", - "packager_packageNoItemsHeader": "Package doesn't contain any items", - "packager_packageNoItemsText": "This package file doesn't contain any items to uninstall.

    You can safely remove this from the system by clicking 'uninstall package' below.", - "packager_packageNoUpgrades": "No upgrades available", - "packager_packageOptions": "Package options", - "packager_packageReadme": "Package readme", - "packager_packageRepository": "Package repository", - "packager_packageUninstallConfirm": "Confirm uninstall", - "packager_packageUninstalledHeader": "Package was uninstalled", - "packager_packageUninstalledText": "The package was successfully uninstalled", - "packager_packageUninstallHeader": "Uninstall package", - "packager_packageUninstallText": "You can unselect items you do not wish to remove, at this time, below. When you click 'confirm uninstall' all checked-off items will be removed.
    Notice: any documents, media etc depending on the items you remove, will stop working, and could lead to system instability, so uninstall with caution. If in doubt, contact the package author.", - "packager_packageUpgradeDownload": "Download update from the repository", - "packager_packageUpgradeHeader": "Upgrade package", - "packager_packageUpgradeInstructions": "Upgrade instructions", - "packager_packageUpgradeText": " There's an upgrade available for this package. You can download it directly from the umbraco package repository.", - "packager_packageVersion": "Package version", - "packager_packageVersionHistory": "Package version history", - "packager_viewPackageWebsite": "View package website", - "paste_doNothing": "Paste with full formatting (Not recommended)", - "paste_errorMessage": "The text you're trying to paste contains special characters or formatting. This could be caused by copying text from Microsoft Word. umbraco can remove special characters or formatting automatically, so the pasted content will be more suitable for the web.", - "paste_removeAll": "Paste as raw text without any formatting at all", - "paste_removeSpecialFormattering": "Paste, but remove formatting (Recommended)", - "publicAccess_paAdvanced": "Role based protection", - "publicAccess_paAdvancedHelp": "If you wish to control access to the page using role-based authentication,
    using umbraco's member groups.", - "publicAccess_paAdvancedNoGroups": "You need to create a membergroup before you can use
    role-based authentication.", - "publicAccess_paErrorPage": "Error Page", - "publicAccess_paErrorPageHelp": "Used when people are logged on, but do not have access", - "publicAccess_paHowWould": "Choose how to restict access to this page", - "publicAccess_paIsProtected": "%0% is now protected", - "publicAccess_paIsRemoved": "Protection removed from %0%", - "publicAccess_paLoginPage": "Login Page", - "publicAccess_paLoginPageHelp": "Choose the page that has the login formular", - "publicAccess_paRemoveProtection": "Remove Protection", - "publicAccess_paSelectPages": "Select the pages that contain login form and error messages", - "publicAccess_paSelectRoles": "Pick the roles who have access to this page", - "publicAccess_paSetLogin": "Set the login and password for this page", - "publicAccess_paSimple": "Single user protection", - "publicAccess_paSimpleHelp": "If you just want to setup simple protection using a single login and password", - "publish_contentPublishedFailedInvalid": " %0% could not be published because these properties: %1% did not pass validation rules. ", - "publish_contentPublishedFailedByEvent": " %0% could not be published, due to a 3rd party extension cancelling the action. ", - "publish_contentPublishedFailedByParent": " %0% can not be published, because a parent page is not published. ", - "publish_includeUnpublished": "Include unpublished child pages", - "publish_inProgress": "Publishing in progress - please wait...", - "publish_inProgressCounter": "%0% out of %1% pages have been published...", - "publish_nodePublish": "%0% has been published", - "publish_nodePublishAll": "%0% and subpages have been published", - "publish_publishAll": "Publish %0% and all its subpages", - "publish_publishHelp": "Click ok to publish %0% and thereby making it's content publicly available.

    You can publish this page and all it's sub-pages by checking publish all children below. ", - "relatedlinks_addExternal": "Add external link", - "relatedlinks_addInternal": "Add internal link", - "relatedlinks_addlink": "Add", - "relatedlinks_caption": "Caption", - "relatedlinks_internalPage": "Internal page", - "relatedlinks_linkurl": "URL", - "relatedlinks_modeDown": "Move Down", - "relatedlinks_modeUp": "Move Up", - "relatedlinks_newWindow": "Open in new window", - "relatedlinks_removeLink": "Remove link", - "rollback_currentVersion": "Current version", - "rollback_diffHelp": "This shows the differences between the current version and the selected version
    Red text will not be shown in the selected version. , green means added", - "rollback_documentRolledBack": "Document has been rolled back", - "rollback_htmlHelp": "This displays the selected version as html, if you wish to see the difference between 2 versions at the same time, use the diff view", - "rollback_rollbackTo": "Rollback to", - "rollback_selectVersion": "Select version", - "rollback_view": "View", - "scripts_editscript": "Edit script file", - "sections_concierge": "Concierge", - "sections_content": "Content", - "sections_courier": "Courier", - "sections_developer": "Developer", - "sections_installer": "Umbraco Configuration Wizard", - "sections_media": "Media", - "sections_member": "Members", - "sections_newsletters": "Newsletters", - "sections_settings": "Settings", - "sections_statistics": "Statistics", - "sections_translation": "Translation", - "sections_users": "Users", - "sections_contour": "Umbraco Contour", - "sections_help": "Help", - "settings_defaulttemplate": "Default template", - "settings_dictionary editor egenskab": "Dictionary Key", - "settings_importDocumentTypeHelp": "To import a document type, find the '.udt' file on your computer by clicking the 'Browse' button and click 'Import' (you'll be asked for confirmation on the next screen)", - "settings_newtabname": "New Tab Title", - "settings_nodetype": "Nodetype", - "settings_objecttype": "Type", - "settings_stylesheet": "Stylesheet", - "settings_stylesheet editor egenskab": "Stylesheet property", - "settings_tab": "Tab", - "settings_tabname": "Tab Title", - "settings_tabs": "Tabs", - "settings_contentTypeEnabled": "Master Content Type enabled", - "settings_contentTypeUses": "This Content Type uses", - "settings_asAContentMasterType": "as a Master Content Type. Tabs from Master Content Types are not shown and can only be edited on the Master Content Type itself", - "settings_noPropertiesDefinedOnTab": "No properties defined on this tab. Click on the 'add a new property' link at the top to create a new property.", - "sort_sortDone": "Sorting complete.", - "sort_sortHelp": "Drag the different items up or down below to set how they should be arranged. Or click the column headers to sort the entire collection of items", - "sort_sortPleaseWait": " Please wait. Items are being sorted, this can take a while.

    Do not close this window during sorting", - "speechBubbles_contentPublishedFailedByEvent": "Publishing was cancelled by a 3rd party add-in", - "speechBubbles_contentTypeDublicatePropertyType": "Property type already exists", - "speechBubbles_contentTypePropertyTypeCreated": "Property type created", - "speechBubbles_contentTypePropertyTypeCreatedText": "Name: %0%
    DataType: %1%", - "speechBubbles_contentTypePropertyTypeDeleted": "Propertytype deleted", - "speechBubbles_contentTypeSavedHeader": "Document Type saved", - "speechBubbles_contentTypeTabCreated": "Tab created", - "speechBubbles_contentTypeTabDeleted": "Tab deleted", - "speechBubbles_contentTypeTabDeletedText": "Tab with id: %0% deleted", - "speechBubbles_cssErrorHeader": "Stylesheet not saved", - "speechBubbles_cssSavedHeader": "Stylesheet saved", - "speechBubbles_cssSavedText": "Stylesheet saved without any errors", - "speechBubbles_dataTypeSaved": "Datatype saved", - "speechBubbles_dictionaryItemSaved": "Dictionary item saved", - "speechBubbles_editContentPublishedFailedByParent": "Publishing failed because the parent page isn't published", - "speechBubbles_editContentPublishedHeader": "Content published", - "speechBubbles_editContentPublishedText": "and visible at the website", - "speechBubbles_editContentSavedHeader": "Content saved", - "speechBubbles_editContentSavedText": "Remember to publish to make changes visible", - "speechBubbles_editContentSendToPublish": "Sent For Approval", - "speechBubbles_editContentSendToPublishText": "Changes have been sent for approval", - "speechBubbles_editMediaSaved": "Media saved", - "speechBubbles_editMediaSavedText": "Media saved without any errors", - "speechBubbles_editMemberSaved": "Member saved", - "speechBubbles_editStylesheetPropertySaved": "Stylesheet Property Saved", - "speechBubbles_editStylesheetSaved": "Stylesheet saved", - "speechBubbles_editTemplateSaved": "Template saved", - "speechBubbles_editUserError": "Error saving user (check log)", - "speechBubbles_editUserSaved": "User Saved", - "speechBubbles_editUserTypeSaved": "User type saved", - "speechBubbles_fileErrorHeader": "File not saved", - "speechBubbles_fileErrorText": "file could not be saved. Please check file permissions", - "speechBubbles_fileSavedHeader": "File saved", - "speechBubbles_fileSavedText": "File saved without any errors", - "speechBubbles_languageSaved": "Language saved", - "speechBubbles_pythonErrorHeader": "Python script not saved", - "speechBubbles_pythonErrorText": "Python script could not be saved due to error", - "speechBubbles_pythonSavedHeader": "Python script saved", - "speechBubbles_pythonSavedText": "No errors in python script", - "speechBubbles_templateErrorHeader": "Template not saved", - "speechBubbles_templateErrorText": "Please make sure that you do not have 2 templates with the same alias", - "speechBubbles_templateSavedHeader": "Template saved", - "speechBubbles_templateSavedText": "Template saved without any errors!", - "speechBubbles_xsltErrorHeader": "XSLT not saved", - "speechBubbles_xsltErrorText": "XSLT contained an error", - "speechBubbles_xsltPermissionErrorText": "XSLT could not be saved, check file permissions", - "speechBubbles_xsltSavedHeader": "XSLT saved", - "speechBubbles_xsltSavedText": "No errors in XSLT", - "speechBubbles_contentUnpublished": "Content unpublished", - "speechBubbles_partialViewSavedHeader": "Partial view saved", - "speechBubbles_partialViewSavedText": "Partial view saved without any errors!", - "speechBubbles_partialViewErrorHeader": "Partial view not saved", - "speechBubbles_partialViewErrorText": "An error occurred saving the file.", - "stylesheet_aliasHelp": "Uses CSS syntax ex: h1, .redHeader, .blueTex", - "stylesheet_editstylesheet": "Edit stylesheet", - "stylesheet_editstylesheetproperty": "Edit stylesheet property", - "stylesheet_nameHelp": "Name to identify the style property in the rich text editor ", - "stylesheet_preview": "Preview", - "stylesheet_styles": "Styles", - "template_edittemplate": "Edit template", - "template_insertContentArea": "Insert content area", - "template_insertContentAreaPlaceHolder": "Insert content area placeholder", - "template_insertDictionaryItem": "Insert dictionary item", - "template_insertMacro": "Insert Macro", - "template_insertPageField": "Insert umbraco page field", - "template_mastertemplate": "Master template", - "template_quickGuide": "Quick Guide to umbraco template tags", - "template_template": "Template", - "templateEditor_alternativeField": "Alternative field", - "templateEditor_alternativeText": "Alternative Text", - "templateEditor_casing": "Casing", - "templateEditor_encoding": "Encoding", - "templateEditor_chooseField": "Choose field", - "templateEditor_convertLineBreaks": "Convert Linebreaks", - "templateEditor_convertLineBreaksHelp": "Replaces linebreaks with html-tag <br>", - "templateEditor_customFields": "Custom Fields", - "templateEditor_dateOnly": "Yes, Date only", - "templateEditor_formatAsDate": "Format as date", - "templateEditor_htmlEncode": "HTML encode", - "templateEditor_htmlEncodeHelp": "Will replace special characters by their HTML equivalent.", - "templateEditor_insertedAfter": "Will be inserted after the field value", - "templateEditor_insertedBefore": "Will be inserted before the field value", - "templateEditor_lowercase": "Lowercase", - "templateEditor_none": "None", - "templateEditor_postContent": "Insert after field", - "templateEditor_preContent": "Insert before field", - "templateEditor_recursive": "Recursive", - "templateEditor_removeParagraph": "Remove Paragraph tags", - "templateEditor_removeParagraphHelp": "Will remove any <P> in the beginning and end of the text", - "templateEditor_standardFields": "Standard Fields", - "templateEditor_uppercase": "Uppercase", - "templateEditor_urlEncode": "URL encode", - "templateEditor_urlEncodeHelp": "Will format special characters in URLs", - "templateEditor_usedIfAllEmpty": "Will only be used when the field values above are empty", - "templateEditor_usedIfEmpty": "This field will only be used if the primary field is empty", - "templateEditor_withTime": "Yes, with time. Seperator: ", - "translation_assignedTasks": "Tasks assigned to you", - "translation_assignedTasksHelp": " The list below shows translation tasks assigned to you. To see a detailed view including comments, click on 'Details' or just the page name. You can also download the page as XML directly by clicking the 'Download Xml' link.
    To close a translation task, please go to the Details view and click the 'Close' button. ", - "translation_closeTask": "close task", - "translation_details": "Translation details", - "translation_downloadAllAsXml": "Download all translation tasks as xml", - "translation_downloadTaskAsXml": "Download xml", - "translation_DownloadXmlDTD": "Download xml DTD", - "translation_fields": "Fields", - "translation_includeSubpages": "Include subpages", - "translation_mailBody": " Hi %0% This is an automated mail to inform you that the document '%1%' has been requested for translation into '%5%' by %2%. Go to http://%3%/translation/details.aspx?id=%4% to edit. Or log into umbraco to get an overview of your translation tasks http://%3% Have a nice day! Cheers from the umbraco robot ", - "translation_mailSubject": "[%0%] Translation task for %1%", - "translation_noTranslators": "No translator users found. Please create a translator user before you start sending content to translation", - "translation_ownedTasks": "Tasks created by you", - "translation_ownedTasksHelp": " The list below shows pages created by you. To see a detailed view including comments, click on 'Details' or just the page name. You can also download the page as XML directly by clicking the 'Download Xml' link. To close a translation task, please go to the Details view and click the 'Close' button. ", - "translation_pageHasBeenSendToTranslation": "The page '%0%' has been send to translation", - "translation_sendToTranslate": "Send the page '%0%' to translation", - "translation_taskAssignedBy": "Assigned by", - "translation_taskOpened": "Task opened", - "translation_totalWords": "Total words", - "translation_translateTo": "Translate to", - "translation_translationDone": "Translation completed.", - "translation_translationDoneHelp": "You can preview the pages, you've just translated, by clicking below. If the original page is found, you will get a comparison of the 2 pages.", - "translation_translationFailed": "Translation failed, the xml file might be corrupt", - "translation_translationOptions": "Translation options", - "translation_translator": "Translator", - "translation_uploadTranslationXml": "Upload translation xml", - "treeHeaders_cacheBrowser": "Cache Browser", - "treeHeaders_contentRecycleBin": "Recycle Bin", - "treeHeaders_createdPackages": "Created packages", - "treeHeaders_datatype": "Data Types", - "treeHeaders_dictionary": "Dictionary", - "treeHeaders_installedPackages": "Installed packages", - "treeHeaders_installSkin": "Install skin", - "treeHeaders_installStarterKit": "Install starter kit", - "treeHeaders_languages": "Languages", - "treeHeaders_localPackage": "Install local package", - "treeHeaders_macros": "Macros", - "treeHeaders_mediaTypes": "Media Types", - "treeHeaders_member": "Members", - "treeHeaders_memberGroup": "Member Groups", - "treeHeaders_memberRoles": "Roles", - "treeHeaders_memberType": "Member Types", - "treeHeaders_nodeTypes": "Document Types", - "treeHeaders_packager": "Packages", - "treeHeaders_packages": "Packages", - "treeHeaders_python": "Python Files", - "treeHeaders_repositories": "Install from repository", - "treeHeaders_runway": "Install Runway", - "treeHeaders_runwayModules": "Runway modules", - "treeHeaders_scripting": "Scripting Files", - "treeHeaders_scripts": "Scripts", - "treeHeaders_stylesheets": "Stylesheets", - "treeHeaders_templates": "Templates", - "treeHeaders_xslt": "XSLT Files", - "update_updateAvailable": "New update ready", - "update_updateDownloadText": "%0% is ready, click here for download", - "update_updateNoServer": "No connection to server", - "update_updateNoServerError": "Error checking for update. Please review trace-stack for further information", - "user_administrators": "Administrator", - "user_categoryField": "Category field", - "user_changePassword": "Change Your Password", - "user_newPassword": "Change Your Password", - "user_confirmNewPassword": "Confirm new password", - "user_changePasswordDescription": "You can change your password for accessing the Umbraco Back Office by filling out the form below and click the 'Change Password' button", - "user_contentChannel": "Content Channel", - "user_descriptionField": "Description field", - "user_disabled": "Disable User", - "user_documentType": "Document Type", - "user_editors": "Editor", - "user_excerptField": "Excerpt field", - "user_language": "Language", - "user_loginname": "Login", - "user_mediastartnode": "Start Node in Media Library", - "user_modules": "Sections", - "user_noConsole": "Disable Umbraco Access", - "user_password": "Password", - "user_resetPassword": "Reset password", - "user_passwordChanged": "Your password has been changed!", - "user_passwordConfirm": "Please confirm the new password", - "user_passwordEnterNew": "Enter your new password", - "user_passwordIsBlank": "Your new password cannot be blank!", - "user_passwordCurrent": "Current password", - "user_passwordInvalid": "Invalid current password", - "user_passwordIsDifferent": "There was a difference between the new password and the confirmed password. Please try again!", - "user_passwordMismatch": "The confirmed password doesn't match the new password!", - "user_permissionReplaceChildren": "Replace child node permssions", - "user_permissionSelectedPages": "You are currently modifying permissions for the pages:", - "user_permissionSelectPages": "Select pages to modify their permissions", - "user_searchAllChildren": "Search all children", - "user_startnode": "Start Node in Content", - "user_username": "Username", - "user_userPermissions": "User permissions", - "user_usertype": "User type", - "user_userTypes": "User types", - "user_writer": "Writer", - "user_yourProfile": "Your profile", - "user_yourHistory": "Your recent history", - "user_sessionExpires": "Session expires in" - }, null]; - } - } - - return { - register: function() { - $httpBackend - .whenGET(mocksUtils.urlRegex('js/language.aspx')) - .respond(getLanguageResource); - } - }; +angular.module('umbraco.mocks'). + factory('localizationMocks', ['$httpBackend', 'mocksUtils', function ($httpBackend, mocksUtils) { + 'use strict'; + + function getLanguageResource(status, data, headers) { + //check for existence of a cookie so we can do login/logout in the belle app (ignore for tests). + if (!mocksUtils.checkAuth()) { + return [401, null, null]; + } + else { + return [200, { + "actions_assignDomain": "Culture and Hostnames", + "actions_auditTrail": "Audit Trail", + "actions_browse": "Browse Node", + "actions_changeDocType": "Change Document Type", + "actions_copy": "Copy", + "actions_create": "Create", + "actions_createPackage": "Create Package", + "actions_delete": "Delete", + "actions_disable": "Disable", + "actions_emptyTrashcan": "Empty recycle bin", + "actions_exportDocumentType": "Export Document Type", + "actions_importDocumentType": "Import Document Type", + "actions_importPackage": "Import Package", + "actions_liveEdit": "Edit in Canvas", + "actions_logout": "Exit", + "actions_move": "Move", + "actions_notify": "Notifications", + "actions_protect": "Public access", + "actions_publish": "Publish", + "actions_unpublish": "Unpublish", + "actions_refreshNode": "Reload nodes", + "actions_republish": "Republish entire site", + "actions_rights": "Permissions", + "actions_rollback": "Rollback", + "actions_sendtopublish": "Send To Publish", + "actions_sendToTranslate": "Send To Translation", + "actions_sort": "Sort", + "actions_toPublish": "Send to publication", + "actions_translate": "Translate", + "actions_update": "Update", + "actions_exportContourForm": "Export form", + "actions_importContourForm": "Import form", + "actions_archiveContourForm": "Archive form", + "actions_unarchiveContourForm": "Unarchive form", + "actions_defaultValue": "Default value", + "assignDomain_permissionDenied": "Permission denied.", + "assignDomain_addNew": "Add new Domain", + "assignDomain_remove": "remove", + "assignDomain_invalidNode": "Invalid node.", + "assignDomain_invalidDomain": "Invalid domain format.", + "assignDomain_duplicateDomain": "Domain has already been assigned.", + "assignDomain_domain": "Domain", + "assignDomain_language": "Language", + "assignDomain_domainCreated": "New domain '%0%' has been created", + "assignDomain_domainDeleted": "Domain '%0%' is deleted", + "assignDomain_domainExists": "Domain '%0%' has already been assigned", + "assignDomain_domainHelp": "Valid domain names are: 'example.com', 'www.example.com', 'example.com:8080' or 'https://www.example.com/'.

    One-level paths in domains are supported, eg. 'example.com/en'. However, they should be avoided. Better use the culture setting above.", + "assignDomain_domainUpdated": "Domain '%0%' has been updated", + "assignDomain_orEdit": "Edit Current Domains", + "assignDomain_inherit": "Inherit", + "assignDomain_setLanguage": "Culture", + "assignDomain_setLanguageHelp": "Set the culture for nodes below the current node,
    or inherit culture from parent nodes. Will also apply
    to the current node, unless a domain below applies too.", + "assignDomain_setDomains": "Domains", + "auditTrails_atViewingFor": "Viewing for", + "buttons_select": "Select", + "buttons_somethingElse": "Do something else", + "buttons_bold": "Bold", + "buttons_deindent": "Cancel Paragraph Indent", + "buttons_formFieldInsert": "Insert form field", + "buttons_graphicHeadline": "Insert graphic headline", + "buttons_htmlEdit": "Edit Html", + "buttons_indent": "Indent Paragraph", + "buttons_italic": "Italic", + "buttons_justifyCenter": "Center", + "buttons_justifyLeft": "Justify Left", + "buttons_justifyRight": "Justify Right", + "buttons_linkInsert": "Insert Link", + "buttons_linkLocal": "Insert local link (anchor)", + "buttons_listBullet": "Bullet List", + "buttons_listNumeric": "Numeric List", + "buttons_macroInsert": "Insert macro", + "buttons_pictureInsert": "Insert picture", + "buttons_relations": "Edit relations", + "buttons_save": "Save", + "buttons_saveAndPublish": "Save and publish", + "buttons_saveToPublish": "Save and send for approval", + "buttons_showPage": "Preview", + "buttons_showPageDisabled": "Preview is disabled because there's no template assigned", + "buttons_styleChoose": "Choose style", + "buttons_styleShow": "Show styles", + "buttons_tableInsert": "Insert table", + "changeDocType_changeDocTypeInstruction": "To change the document type for the selected content, first select from the list of valid types for this location.", + "changeDocType_changeDocTypeInstruction2": "Then confirm and/or amend the mapping of properties from the current type to the new, and click Save.", + "changeDocType_contentRepublished": "The content has been re-published.", + "changeDocType_currentProperty": "Current Property", + "changeDocType_currentType": "Current type", + "changeDocType_docTypeCannotBeChanged": "The document type cannot be changed, as there are no alternatives valid for this location.", + "changeDocType_docTypeChanged": "Document Type Changed", + "changeDocType_mapProperties": "Map Properties", + "changeDocType_mapToProperty": "Map to Property", + "changeDocType_newTemplate": "New Template", + "changeDocType_newType": "New Type", + "changeDocType_none": "none", + "changeDocType_selectedContent": "Content", + "changeDocType_selectNewDocType": "Select New Document Type", + "changeDocType_successMessage": "The document type of the selected content has been successfully changed to [new type] and the following properties mapped:", + "changeDocType_to": "to", + "changeDocType_validationErrorPropertyWithMoreThanOneMapping": "Could not complete property mapping as one or more properties have more than one mapping defined.", + "changeDocType_validDocTypesNote": "Only alternate types valid for the current location are displayed.", + "content_about": "About this page", + "content_alias": "Alias", + "content_alternativeTextHelp": "(how would you describe the picture over the phone)", + "content_alternativeUrls": "Alternative Links", + "content_clickToEdit": "Click to edit this item", + "content_createBy": "Created by", + "content_createByDesc": "Original autho", + "content_updatedBy": "Updated by", + "content_createDate": "Created", + "content_createDateDesc": "Date/time this document was created", + "content_documentType": "Document Type", + "content_editing": "Editing", + "content_expireDate": "Remove at", + "content_itemChanged": "This item has been changed after publication", + "content_itemNotPublished": "This item is not published", + "content_lastPublished": "Last published", + "content_mediatype": "Media Type", + "content_mediaLinks": "Link to media item(s)", + "content_membergroup": "Member Group", + "content_memberrole": "Role", + "content_membertype": "Member Type", + "content_noDate": "No date chosen", + "content_nodeName": "Page Title", + "content_otherElements": "Properties", + "content_parentNotPublished": "This document is published but is not visible because the parent '%0%' is unpublished", + "content_parentNotPublishedAnomaly": "Oops: this document is published but is not in the cache (internal error)", + "content_publish": "Publish", + "content_publishStatus": "Publication Status", + "content_releaseDate": "Publish at", + "content_removeDate": "Clear Date", + "content_sortDone": "Sortorder is updated", + "content_sortHelp": "To sort the nodes, simply drag the nodes or click one of the column headers. You can select multiple nodes by holding the 'shift' or 'control' key while selecting", + "content_statistics": "Statistics", + "content_titleOptional": "Title (optional)", + "content_type": "Type", + "content_unPublish": "Unpublish", + "content_updateDate": "Last edited", + "content_updateDateDesc": "Date/time this document was created", + "content_uploadClear": "Remove file", + "content_urls": "Link to document", + "content_memberof": "Member of group(s)", + "content_notmemberof": "Not a member of group(s)", + "content_childItems": "Child items", + "create_chooseNode": "Where do you want to create the new %0%", + "create_createUnder": "Create a page under", + "create_updateData": "Choose a type and a title", + "create_noDocumentTypes": "There are no allowed document types available. You must enable these in the settings section under 'document types'.", + "create_noMediaTypes": "There are no allowed media types available. You must enable these in the settings section under 'media types'.", + "dashboard_browser": "Browse your website", + "dashboard_dontShowAgain": "- Hide", + "dashboard_nothinghappens": "If umbraco isn't opening, you might need to allow popups from this site", + "dashboard_openinnew": "has opened in a new window", + "dashboard_restart": "Restart", + "dashboard_visit": "Visit", + "dashboard_welcome": "Welcome", + "defaultdialogs_anchorInsert": "Name", + "defaultdialogs_assignDomain": "Manage hostnames", + "defaultdialogs_closeThisWindow": "Close this window", + "defaultdialogs_confirmdelete": "Are you sure you want to delete", + "defaultdialogs_confirmdisable": "Are you sure you want to disable", + "defaultdialogs_confirmEmptyTrashcan": "Please check this box to confirm deletion of %0% item(s)", + "defaultdialogs_confirmlogout": "Are you sure?", + "defaultdialogs_confirmSure": "Are you sure?", + "defaultdialogs_cut": "Cut", + "defaultdialogs_editdictionary": "Edit Dictionary Item", + "defaultdialogs_editlanguage": "Edit Language", + "defaultdialogs_insertAnchor": "Insert local link", + "defaultdialogs_insertCharacter": "Insert character", + "defaultdialogs_insertgraphicheadline": "Insert graphic headline", + "defaultdialogs_insertimage": "Insert picture", + "defaultdialogs_insertlink": "Insert link", + "defaultdialogs_insertMacro": "Click to add a Macro", + "defaultdialogs_inserttable": "Insert table", + "defaultdialogs_lastEdited": "Last Edited", + "defaultdialogs_link": "Link", + "defaultdialogs_linkinternal": "Internal link:", + "defaultdialogs_linklocaltip": "When using local links, insert '#' infront of link", + "defaultdialogs_linknewwindow": "Open in new window?", + "defaultdialogs_macroContainerSettings": "Macro Settings", + "defaultdialogs_macroDoesNotHaveProperties": "This macro does not contain any properties you can edit", + "defaultdialogs_paste": "Paste", + "defaultdialogs_permissionsEdit": "Edit Permissions for", + "defaultdialogs_recycleBinDeleting": "The items in the recycle bin are now being deleted. Please do not close this window while this operation takes place", + "defaultdialogs_recycleBinIsEmpty": "The recycle bin is now empty", + "defaultdialogs_recycleBinWarning": "When items are deleted from the recycle bin, they will be gone forever", + "defaultdialogs_regexSearchError": "regexlib.com's webservice is currently experiencing some problems, which we have no control over. We are very sorry for this inconvenience.", + "defaultdialogs_regexSearchHelp": "Search for a regular expression to add validation to a form field. Exemple: 'email, 'zip-code' 'url'", + "defaultdialogs_removeMacro": "Remove Macro", + "defaultdialogs_requiredField": "Required Field", + "defaultdialogs_sitereindexed": "Site is reindexed", + "defaultdialogs_siterepublished": "The website cache has been refreshed. All publish content is now uptodate. While all unpublished content is still unpublished", + "defaultdialogs_siterepublishHelp": "The website cache will be refreshed. All published content will be updated, while unpublished content will stay unpublished.", + "defaultdialogs_tableColumns": "Number of columns", + "defaultdialogs_tableRows": "Number of rows", + "defaultdialogs_templateContentAreaHelp": "Set a placeholder id by setting an ID on your placeholder you can inject content into this template from child templates, by refering this ID using a <asp:content /> element.", + "defaultdialogs_templateContentPlaceHolderHelp": "Select a placeholder id from the list below. You can only choose Id's from the current template's master.", + "defaultdialogs_thumbnailimageclickfororiginal": "Click on the image to see full size", + "defaultdialogs_treepicker": "Pick item", + "defaultdialogs_viewCacheItem": "View Cache Item", + "dictionaryItem_description": " Edit the different language versions for the dictionary item '%0%' below
    You can add additional languages under the 'languages' in the menu on the left ", + "dictionaryItem_displayName": "Culture Name", + "placeholders_username": "Enter your username", + "placeholders_password": "Enter your password", + "placeholders_entername": "Enter a name...", + "placeholders_nameentity": "Name the %0%...", + "placeholders_search": "Type to search...", + "placeholders_filter": "Type to filter...", + "editcontenttype_allowedchildnodetypes": "Allowed child nodetypes", + "editcontenttype_create": "Create", + "editcontenttype_deletetab": "Delete tab", + "editcontenttype_description": "Description", + "editcontenttype_newtab": "New tab", + "editcontenttype_tab": "Tab", + "editcontenttype_thumbnail": "Thumbnail", + "editcontenttype_iscontainercontenttype": "Use as container content type", + "editdatatype_addPrevalue": "Add prevalue", + "editdatatype_dataBaseDatatype": "Database datatype", + "editdatatype_guid": "Property editor GUID", + "editdatatype_renderControl": "Property editor", + "editdatatype_rteButtons": "Buttons", + "editdatatype_rteEnableAdvancedSettings": "Enable advanced settings for", + "editdatatype_rteEnableContextMenu": "Enable context menu", + "editdatatype_rteMaximumDefaultImgSize": "Maximum default size of inserted images", + "editdatatype_rteRelatedStylesheets": "Related stylesheets", + "editdatatype_rteShowLabel": "Show label", + "editdatatype_rteWidthAndHeight": "Width and height", + "errorHandling_errorButDataWasSaved": "Your data has been saved, but before you can publish this page there are some errors you need to fix first:", + "errorHandling_errorChangingProviderPassword": "The current MemberShip Provider does not support changing password (EnablePasswordRetrieval need to be true)", + "errorHandling_errorExistsWithoutTab": "%0% already exists", + "errorHandling_errorHeader": "There were errors:", + "errorHandling_errorHeaderWithoutTab": "There were errors:", + "errorHandling_errorInPasswordFormat": "The password should be a minimum of %0% characters long and contain at least %1% non-alpha numeric character(s)", + "errorHandling_errorIntegerWithoutTab": "%0% must be an integer", + "errorHandling_errorMandatory": "The %0% field in the %1% tab is mandatory", + "errorHandling_errorMandatoryWithoutTab": "%0% is a mandatory field", + "errorHandling_errorRegExp": "%0% at %1% is not in a correct format", + "errorHandling_errorRegExpWithoutTab": "%0% is not in a correct format", + "errors_dissallowedMediaType": "The specified file type has been dissallowed by the administrator", + "errors_codemirroriewarning": "NOTE! Even though CodeMirror is enabled by configuration, it is disabled in Internet Explorer because it's not stable enough.", + "errors_contentTypeAliasAndNameNotNull": "Please fill both alias and name on the new propertytype!", + "errors_filePermissionsError": "There is a problem with read/write access to a specific file or folder", + "errors_missingTitle": "Please enter a title", + "errors_missingType": "Please choose a type", + "errors_pictureResizeBiggerThanOrg": "You're about to make the picture larger than the original size. Are you sure that you want to proceed?", + "errors_pythonErrorHeader": "Error in python script", + "errors_pythonErrorText": "The python script has not been saved, because it contained error(s)", + "errors_startNodeDoesNotExists": "Startnode deleted, please contact your administrator", + "errors_stylesMustMarkBeforeSelect": "Please mark content before changing style", + "errors_stylesNoStylesOnPage": "No active styles available", + "errors_tableColMergeLeft": "Please place cursor at the left of the two cells you wish to merge", + "errors_tableSplitNotSplittable": "You cannot split a cell that hasn't been merged.", + "errors_xsltErrorHeader": "Error in XSLT source", + "errors_xsltErrorText": "The XSLT has not been saved, because it contained error(s)", + "general_about": "About", + "general_action": "Action", + "general_add": "Add", + "general_alias": "Alias", + "general_areyousure": "Are you sure?", + "general_border": "Border", + "general_by": "or", + "general_cancel": "Cancel", + "general_cellMargin": "Cell margin", + "general_choose": "Choose", + "general_close": "Close", + "general_closewindow": "Close Window", + "general_comment": "Comment", + "general_confirm": "Confirm", + "general_constrainProportions": "Constrain proportions", + "general_continue": "Continue", + "general_copy": "Copy", + "general_create": "Create", + "general_database": "Database", + "general_date": "Date", + "general_default": "Default", + "general_delete": "Delete", + "general_deleted": "Deleted", + "general_deleting": "Deleting...", + "general_design": "Design", + "general_dimensions": "Dimensions", + "general_down": "Down", + "general_download": "Download", + "general_edit": "Edit", + "general_edited": "Edited", + "general_elements": "Elements", + "general_email": "Email", + "general_error": "Error", + "general_findDocument": "Find", + "general_height": "Height", + "general_help": "Help", + "general_icon": "Icon", + "general_import": "Import", + "general_innerMargin": "Inner margin", + "general_insert": "Insert", + "general_install": "Install", + "general_justify": "Justify", + "general_language": "Language", + "general_layout": "Layout", + "general_loading": "Loading", + "general_locked": "Locked", + "general_login": "Login", + "general_logoff": "Log off", + "general_logout": "Logout", + "general_macro": "Macro", + "general_move": "Move", + "general_name": "Name", + "general_new": "New", + "general_next": "Next", + "general_no": "No", + "general_of": "of", + "general_ok": "OK", + "general_open": "Open", + "general_or": "or", + "general_password": "Password", + "general_path": "Path", + "general_placeHolderID": "Placeholder ID", + "general_pleasewait": "One moment please...", + "general_previous": "Previous", + "general_properties": "Properties", + "general_reciept": "Email to receive form data", + "general_recycleBin": "Recycle Bin", + "general_remaining": "Remaining", + "general_rename": "Rename", + "general_renew": "Renew", + "general_required": "Required", + "general_retry": "Retry", + "general_rights": "Permissions", + "general_search": "Search", + "general_server": "Server", + "general_show": "Show", + "general_showPageOnSend": "Show page on Send", + "general_size": "Size", + "general_sort": "Sort", + "general_type": "Type", + "general_typeToSearch": "Type to search...", + "general_up": "Up", + "general_update": "Update", + "general_upgrade": "Upgrade", + "general_upload": "Upload", + "general_url": "Url", + "general_user": "User", + "general_username": "Username", + "general_value": "Value", + "general_view": "View", + "general_welcome": "Welcome...", + "general_width": "Width", + "general_yes": "Yes", + "general_folder": "Folder", + "general_searchResults": "Search results", + "graphicheadline_backgroundcolor": "Background color", + "graphicheadline_bold": "Bold", + "graphicheadline_color": "Text color", + "graphicheadline_font": "Font", + "graphicheadline_text": "Text", + "headers_page": "Page", + "installer_databaseErrorCannotConnect": "The installer cannot connect to the database.", + "installer_databaseErrorWebConfig": "Could not save the web.config file. Please modify the connection string manually.", + "installer_databaseFound": "Your database has been found and is identified as", + "installer_databaseHeader": "Database configuration", + "installer_databaseInstall": " Press the install button to install the Umbraco %0% database ", + "installer_databaseInstallDone": "Umbraco %0% has now been copied to your database. Press Next to proceed.", + "installer_databaseNotFound": "

    Database not found! Please check that the information in the 'connection string' of the \"web.config\" file is correct.

    To proceed, please edit the 'web.config' file (using Visual Studio or your favourite text editor), scroll to the bottom, add the connection string for your database in the key named 'umbracoDbDSN' and save the file.

    Click the retry button when done.
    More information on editing web.config here.

    ", + "installer_databaseText": "To complete this step, you must know some information regarding your database server ('connection string').
    Please contact your ISP if necessary. If you're installing on a local machine or server you might need information from your system administrator.", + "installer_databaseUpgrade": "

    Press the upgrade button to upgrade your database to Umbraco %0%

    Don't worry - no content will be deleted and everything will continue working afterwards!

    ", + "installer_databaseUpgradeDone": "Your database has been upgraded to the final version %0%.
    Press Next to proceed. ", + "installer_databaseUpToDate": "Your current database is up-to-date!. Click next to continue the configuration wizard", + "installer_defaultUserChangePass": "The Default users' password needs to be changed!", + "installer_defaultUserDisabled": "The Default user has been disabled or has no access to umbraco!

    No further actions needs to be taken. Click Next to proceed.", + "installer_defaultUserPassChanged": "The Default user's password has been successfully changed since the installation!

    No further actions needs to be taken. Click Next to proceed.", + "installer_defaultUserPasswordChanged": "The password is changed!", + "installer_defaultUserText": "

    umbraco creates a default user with a login ('admin') and password ('default'). It's important that the password is changed to something unique.

    This step will check the default user's password and suggest if it needs to be changed.

    ", + "installer_greatStart": "Get a great start, watch our introduction videos", + "installer_licenseText": "By clicking the next button (or modifying the umbracoConfigurationStatus in web.config), you accept the license for this software as specified in the box below. Notice that this umbraco distribution consists of two different licenses, the open source MIT license for the framework and the umbraco freeware license that covers the UI.", + "installer_None": "Not installed yet.", + "installer_permissionsAffectedFolders": "Affected files and folders", + "installer_permissionsAffectedFoldersMoreInfo": "More information on setting up permissions for umbraco here", + "installer_permissionsAffectedFoldersText": "You need to grant ASP.NET modify permissions to the following files/folders", + "installer_permissionsAlmostPerfect": "Your permission settings are almost perfect!

    You can run umbraco without problems, but you will not be able to install packages which are recommended to take full advantage of umbraco.", + "installer_permissionsHowtoResolve": "How to Resolve", + "installer_permissionsHowtoResolveLink": "Click here to read the text version", + "installer_permissionsHowtoResolveText": "Watch our video tutorial on setting up folder permissions for umbraco or read the text version.", + "installer_permissionsMaybeAnIssue": "Your permission settings might be an issue!

    You can run umbraco without problems, but you will not be able to create folders or install packages which are recommended to take full advantage of umbraco.", + "installer_permissionsNotReady": "Your permission settings are not ready for umbraco!

    In order to run umbraco, you'll need to update your permission settings.", + "installer_permissionsPerfect": "Your permission settings are perfect!

    You are ready to run umbraco and install packages!", + "installer_permissionsResolveFolderIssues": "Resolving folder issue", + "installer_permissionsResolveFolderIssuesLink": "Follow this link for more information on problems with ASP.NET and creating folders", + "installer_permissionsSettingUpPermissions": "Setting up folder permissions", + "installer_permissionsText": " umbraco needs write/modify access to certain directories in order to store files like pictures and PDF's. It also stores temporary data (aka: cache) for enhancing the performance of your website. ", + "installer_runwayFromScratch": "I want to start from scratch", + "installer_runwayFromScratchText": " Your website is completely empty at the moment, so that's perfect if you want to start from scratch and create your own document types and templates. (learn how) You can still choose to install Runway later on. Please go to the Developer section and choose Packages. ", + "installer_runwayHeader": "You've just set up a clean Umbraco platform. What do you want to do next?", + "installer_runwayInstalled": "Runway is installed", + "installer_runwayInstalledText": " You have the foundation in place. Select what modules you wish to install on top of it.
    This is our list of recommended modules, check off the ones you would like to install, or view the full list of modules ", + "installer_runwayOnlyProUsers": "Only recommended for experienced users", + "installer_runwaySimpleSite": "I want to start with a simple website", + "installer_runwaySimpleSiteText": "

    'Runway' is a simple website providing some basic document types and templates. The installer can set up Runway for you automatically, but you can easily edit, extend or remove it. It's not necessary and you can perfectly use Umbraco without it. However, Runway offers an easy foundation based on best practices to get you started faster than ever. If you choose to install Runway, you can optionally select basic building blocks called Runway Modules to enhance your Runway pages.

    Included with Runway: Home page, Getting Started page, Installing Modules page.
    Optional Modules: Top Navigation, Sitemap, Contact, Gallery.
    ", + "installer_runwayWhatIsRunway": "What is Runway", + "installer_step1": "Step 1/5 Accept license", + "installer_step2": "Step 2/5: Database configuration", + "installer_step3": "Step 3/5: Validating File Permissions", + "installer_step4": "Step 4/5: Check umbraco security", + "installer_step5": "Step 5/5: Umbraco is ready to get you started", + "installer_thankYou": "Thank you for choosing umbraco", + "installer_theEndBrowseSite": "

    Browse your new site

    You installed Runway, so why not see how your new website looks.", + "installer_theEndFurtherHelp": "

    Further help and information

    Get help from our award winning community, browse the documentation or watch some free videos on how to build a simple site, how to use packages and a quick guide to the umbraco terminology", + "installer_theEndHeader": "Umbraco %0% is installed and ready for use", + "installer_theEndInstallFailed": "To finish the installation, you'll need to manually edit the /web.config file and update the AppSetting key umbracoConfigurationStatus in the bottom to the value of '%0%'.", + "installer_theEndInstallSuccess": "You can get started instantly by clicking the 'Launch Umbraco' button below.
    If you are new to umbraco, you can find plenty of resources on our getting started pages.", + "installer_theEndOpenUmbraco": "

    Launch Umbraco

    To manage your website, simply open the umbraco back office and start adding content, updating the templates and stylesheets or add new functionality", + "installer_Unavailable": "Connection to database failed.", + "installer_Version3": "Umbraco Version 3", + "installer_Version4": "Umbraco Version 4", + "installer_watch": "Watch", + "installer_welcomeIntro": "This wizard will guide you through the process of configuring umbraco %0% for a fresh install or upgrading from version 3.0.

    Press 'next' to start the wizard.", + "language_cultureCode": "Culture Code", + "language_displayName": "Culture Name", + "lockout_lockoutWillOccur": "You've been idle and logout will automatically occur in", + "lockout_renewSession": "Renew now to save your work", + "login_greeting1": "Happy super sunday", + "login_greeting2": "Happy manic monday ", + "login_greeting3": "Happy tremendous tuesday", + "login_greeting4": "Happy wonderfull wednesday", + "login_greeting5": "Happy thunder thursday", + "login_greeting6": "Happy friendly friday", + "login_greeting7": "Happy shiny saturday", + "login_instruction": "log in below:", + "login_bottomText": "

    © 2001 - %0%
    umbraco.org

    ", + "main_dashboard": "Dashboard", + "main_sections": "Sections", + "main_tree": "Content", + "moveOrCopy_choose": "Choose page above...", + "moveOrCopy_copyDone": "%0% has been copied to %1%", + "moveOrCopy_copyTo": "Select where the document %0% should be copied to below", + "moveOrCopy_moveDone": "%0% has been moved to %1%", + "moveOrCopy_moveTo": "Select where the document %0% should be moved to below", + "moveOrCopy_nodeSelected": "has been selected as the root of your new content, click 'ok' below.", + "moveOrCopy_noNodeSelected": "No node selected yet, please select a node in the list above before clicking 'ok'", + "moveOrCopy_notAllowedByContentType": "The current node is not allowed under the chosen node because of its type", + "moveOrCopy_notAllowedByPath": "The current node cannot be moved to one of its subpages", + "moveOrCopy_notAllowedAtRoot": "The current node cannot exist at the root", + "moveOrCopy_notValid": "The action isn't allowed since you have insufficient permissions on 1 or more child documents.", + "moveOrCopy_relateToOriginal": "Relate copied items to original", + "notifications_editNotifications": "Edit your notification for %0%", + "notifications_mailBody": " Hi %0% This is an automated mail to inform you that the task '%1%' has been performed on the page '%2%' by the user '%3%' Go to http://%4%/actions/editContent.aspx?id=%5% to edit. Have a nice day! Cheers from the umbraco robot ", + "notifications_mailBodyHtml": "

    Hi %0%

    This is an automated mail to inform you that the task '%1%' has been performed on the page '%2%' by the user '%3%'

    Update summary:

    %6%

    Have a nice day!

    Cheers from the umbraco robot

    ", + "notifications_mailSubject": "[%0%] Notification about %1% performed on %2%", + "notifications_notifications": "Notifications", + "packager_chooseLocalPackageText": " Choose Package from your machine, by clicking the Browse
    button and locating the package. umbraco packages usually have a '.umb' or '.zip' extension. ", + "packager_packageAuthor": "Author", + "packager_packageDemonstration": "Demonstration", + "packager_packageDocumentation": "Documentation", + "packager_packageMetaData": "Package meta data", + "packager_packageName": "Package name", + "packager_packageNoItemsHeader": "Package doesn't contain any items", + "packager_packageNoItemsText": "This package file doesn't contain any items to uninstall.

    You can safely remove this from the system by clicking 'uninstall package' below.", + "packager_packageNoUpgrades": "No upgrades available", + "packager_packageOptions": "Package options", + "packager_packageReadme": "Package readme", + "packager_packageRepository": "Package repository", + "packager_packageUninstallConfirm": "Confirm uninstall", + "packager_packageUninstalledHeader": "Package was uninstalled", + "packager_packageUninstalledText": "The package was successfully uninstalled", + "packager_packageUninstallHeader": "Uninstall package", + "packager_packageUninstallText": "You can unselect items you do not wish to remove, at this time, below. When you click 'confirm uninstall' all checked-off items will be removed.
    Notice: any documents, media etc depending on the items you remove, will stop working, and could lead to system instability, so uninstall with caution. If in doubt, contact the package author.", + "packager_packageUpgradeDownload": "Download update from the repository", + "packager_packageUpgradeHeader": "Upgrade package", + "packager_packageUpgradeInstructions": "Upgrade instructions", + "packager_packageUpgradeText": " There's an upgrade available for this package. You can download it directly from the umbraco package repository.", + "packager_packageVersion": "Package version", + "packager_packageVersionHistory": "Package version history", + "packager_viewPackageWebsite": "View package website", + "paste_doNothing": "Paste with full formatting (Not recommended)", + "paste_errorMessage": "The text you're trying to paste contains special characters or formatting. This could be caused by copying text from Microsoft Word. umbraco can remove special characters or formatting automatically, so the pasted content will be more suitable for the web.", + "paste_removeAll": "Paste as raw text without any formatting at all", + "paste_removeSpecialFormattering": "Paste, but remove formatting (Recommended)", + "publicAccess_paAdvanced": "Role based protection", + "publicAccess_paAdvancedHelp": "If you wish to control access to the page using role-based authentication,
    using umbraco's member groups.", + "publicAccess_paAdvancedNoGroups": "You need to create a membergroup before you can use
    role-based authentication.", + "publicAccess_paErrorPage": "Error Page", + "publicAccess_paErrorPageHelp": "Used when people are logged on, but do not have access", + "publicAccess_paHowWould": "Choose how to restict access to this page", + "publicAccess_paIsProtected": "%0% is now protected", + "publicAccess_paIsRemoved": "Protection removed from %0%", + "publicAccess_paLoginPage": "Login Page", + "publicAccess_paLoginPageHelp": "Choose the page that has the login formular", + "publicAccess_paRemoveProtection": "Remove Protection", + "publicAccess_paSelectPages": "Select the pages that contain login form and error messages", + "publicAccess_paSelectRoles": "Pick the roles who have access to this page", + "publicAccess_paSetLogin": "Set the login and password for this page", + "publicAccess_paSimple": "Single user protection", + "publicAccess_paSimpleHelp": "If you just want to setup simple protection using a single login and password", + "publish_contentPublishedFailedInvalid": " %0% could not be published because these properties: %1% did not pass validation rules. ", + "publish_contentPublishedFailedByEvent": " %0% could not be published, due to a 3rd party extension cancelling the action. ", + "publish_contentPublishedFailedByParent": " %0% can not be published, because a parent page is not published. ", + "publish_includeUnpublished": "Include unpublished child pages", + "publish_inProgress": "Publishing in progress - please wait...", + "publish_inProgressCounter": "%0% out of %1% pages have been published...", + "publish_nodePublish": "%0% has been published", + "publish_nodePublishAll": "%0% and subpages have been published", + "publish_publishAll": "Publish %0% and all its subpages", + "publish_publishHelp": "Click ok to publish %0% and thereby making it's content publicly available.

    You can publish this page and all it's sub-pages by checking publish all children below. ", + "relatedlinks_addExternal": "Add external link", + "relatedlinks_addInternal": "Add internal link", + "relatedlinks_addlink": "Add", + "relatedlinks_caption": "Caption", + "relatedlinks_internalPage": "Internal page", + "relatedlinks_linkurl": "URL", + "relatedlinks_modeDown": "Move Down", + "relatedlinks_modeUp": "Move Up", + "relatedlinks_newWindow": "Open in new window", + "relatedlinks_removeLink": "Remove link", + "rollback_currentVersion": "Current version", + "rollback_diffHelp": "This shows the differences between the current version and the selected version
    Red text will not be shown in the selected version. , green means added", + "rollback_documentRolledBack": "Document has been rolled back", + "rollback_htmlHelp": "This displays the selected version as html, if you wish to see the difference between 2 versions at the same time, use the diff view", + "rollback_rollbackTo": "Rollback to", + "rollback_selectVersion": "Select version", + "rollback_view": "View", + "scripts_editscript": "Edit script file", + "sections_concierge": "Concierge", + "sections_content": "Content", + "sections_courier": "Courier", + "sections_developer": "Developer", + "sections_installer": "Umbraco Configuration Wizard", + "sections_media": "Media", + "sections_member": "Members", + "sections_newsletters": "Newsletters", + "sections_settings": "Settings", + "sections_statistics": "Statistics", + "sections_translation": "Translation", + "sections_users": "Users", + "sections_contour": "Umbraco Contour", + "sections_help": "Help", + "settings_defaulttemplate": "Default template", + "settings_dictionary editor egenskab": "Dictionary Key", + "settings_importDocumentTypeHelp": "To import a document type, find the '.udt' file on your computer by clicking the 'Browse' button and click 'Import' (you'll be asked for confirmation on the next screen)", + "settings_newtabname": "New Tab Title", + "settings_nodetype": "Nodetype", + "settings_objecttype": "Type", + "settings_stylesheet": "Stylesheet", + "settings_stylesheet editor egenskab": "Stylesheet property", + "settings_tab": "Tab", + "settings_tabname": "Tab Title", + "settings_tabs": "Tabs", + "settings_contentTypeEnabled": "Master Content Type enabled", + "settings_contentTypeUses": "This Content Type uses", + "settings_asAContentMasterType": "as a Master Content Type. Tabs from Master Content Types are not shown and can only be edited on the Master Content Type itself", + "settings_noPropertiesDefinedOnTab": "No properties defined on this tab. Click on the 'add a new property' link at the top to create a new property.", + "sort_sortDone": "Sorting complete.", + "sort_sortHelp": "Drag the different items up or down below to set how they should be arranged. Or click the column headers to sort the entire collection of items", + "sort_sortPleaseWait": " Please wait. Items are being sorted, this can take a while.

    Do not close this window during sorting", + "speechBubbles_contentPublishedFailedByEvent": "Publishing was cancelled by a 3rd party add-in", + "speechBubbles_contentTypeDublicatePropertyType": "Property type already exists", + "speechBubbles_contentTypePropertyTypeCreated": "Property type created", + "speechBubbles_contentTypePropertyTypeCreatedText": "Name: %0%
    DataType: %1%", + "speechBubbles_contentTypePropertyTypeDeleted": "Propertytype deleted", + "speechBubbles_contentTypeSavedHeader": "Document Type saved", + "speechBubbles_contentTypeTabCreated": "Tab created", + "speechBubbles_contentTypeTabDeleted": "Tab deleted", + "speechBubbles_contentTypeTabDeletedText": "Tab with id: %0% deleted", + "speechBubbles_cssErrorHeader": "Stylesheet not saved", + "speechBubbles_cssSavedHeader": "Stylesheet saved", + "speechBubbles_cssSavedText": "Stylesheet saved without any errors", + "speechBubbles_dataTypeSaved": "Datatype saved", + "speechBubbles_dictionaryItemSaved": "Dictionary item saved", + "speechBubbles_editContentPublishedFailedByParent": "Publishing failed because the parent page isn't published", + "speechBubbles_editContentPublishedHeader": "Content published", + "speechBubbles_editContentPublishedText": "and visible at the website", + "speechBubbles_editContentSavedHeader": "Content saved", + "speechBubbles_editContentSavedText": "Remember to publish to make changes visible", + "speechBubbles_editContentSendToPublish": "Sent For Approval", + "speechBubbles_editContentSendToPublishText": "Changes have been sent for approval", + "speechBubbles_editMediaSaved": "Media saved", + "speechBubbles_editMediaSavedText": "Media saved without any errors", + "speechBubbles_editMemberSaved": "Member saved", + "speechBubbles_editStylesheetPropertySaved": "Stylesheet Property Saved", + "speechBubbles_editStylesheetSaved": "Stylesheet saved", + "speechBubbles_editTemplateSaved": "Template saved", + "speechBubbles_editUserError": "Error saving user (check log)", + "speechBubbles_editUserSaved": "User Saved", + "speechBubbles_editUserTypeSaved": "User type saved", + "speechBubbles_fileErrorHeader": "File not saved", + "speechBubbles_fileErrorText": "file could not be saved. Please check file permissions", + "speechBubbles_fileSavedHeader": "File saved", + "speechBubbles_fileSavedText": "File saved without any errors", + "speechBubbles_languageSaved": "Language saved", + "speechBubbles_pythonErrorHeader": "Python script not saved", + "speechBubbles_pythonErrorText": "Python script could not be saved due to error", + "speechBubbles_pythonSavedHeader": "Python script saved", + "speechBubbles_pythonSavedText": "No errors in python script", + "speechBubbles_templateErrorHeader": "Template not saved", + "speechBubbles_templateErrorText": "Please make sure that you do not have 2 templates with the same alias", + "speechBubbles_templateSavedHeader": "Template saved", + "speechBubbles_templateSavedText": "Template saved without any errors!", + "speechBubbles_xsltErrorHeader": "XSLT not saved", + "speechBubbles_xsltErrorText": "XSLT contained an error", + "speechBubbles_xsltPermissionErrorText": "XSLT could not be saved, check file permissions", + "speechBubbles_xsltSavedHeader": "XSLT saved", + "speechBubbles_xsltSavedText": "No errors in XSLT", + "speechBubbles_contentUnpublished": "Content unpublished", + "speechBubbles_partialViewSavedHeader": "Partial view saved", + "speechBubbles_partialViewSavedText": "Partial view saved without any errors!", + "speechBubbles_partialViewErrorHeader": "Partial view not saved", + "speechBubbles_partialViewErrorText": "An error occurred saving the file.", + "stylesheet_aliasHelp": "Uses CSS syntax ex: h1, .redHeader, .blueTex", + "stylesheet_editstylesheet": "Edit stylesheet", + "stylesheet_editstylesheetproperty": "Edit stylesheet property", + "stylesheet_nameHelp": "Name to identify the style property in the rich text editor ", + "stylesheet_preview": "Preview", + "stylesheet_styles": "Styles", + "template_edittemplate": "Edit template", + "template_insertContentArea": "Insert content area", + "template_insertContentAreaPlaceHolder": "Insert content area placeholder", + "template_insertDictionaryItem": "Insert dictionary item", + "template_insertMacro": "Insert Macro", + "template_insertPageField": "Insert umbraco page field", + "template_mastertemplate": "Master template", + "template_quickGuide": "Quick Guide to umbraco template tags", + "template_template": "Template", + "templateEditor_alternativeField": "Alternative field", + "templateEditor_alternativeText": "Alternative Text", + "templateEditor_casing": "Casing", + "templateEditor_encoding": "Encoding", + "templateEditor_chooseField": "Choose field", + "templateEditor_convertLineBreaks": "Convert Linebreaks", + "templateEditor_convertLineBreaksHelp": "Replaces linebreaks with html-tag <br>", + "templateEditor_customFields": "Custom Fields", + "templateEditor_dateOnly": "Yes, Date only", + "templateEditor_formatAsDate": "Format as date", + "templateEditor_htmlEncode": "HTML encode", + "templateEditor_htmlEncodeHelp": "Will replace special characters by their HTML equivalent.", + "templateEditor_insertedAfter": "Will be inserted after the field value", + "templateEditor_insertedBefore": "Will be inserted before the field value", + "templateEditor_lowercase": "Lowercase", + "templateEditor_none": "None", + "templateEditor_postContent": "Insert after field", + "templateEditor_preContent": "Insert before field", + "templateEditor_recursive": "Recursive", + "templateEditor_removeParagraph": "Remove Paragraph tags", + "templateEditor_removeParagraphHelp": "Will remove any <P> in the beginning and end of the text", + "templateEditor_standardFields": "Standard Fields", + "templateEditor_uppercase": "Uppercase", + "templateEditor_urlEncode": "URL encode", + "templateEditor_urlEncodeHelp": "Will format special characters in URLs", + "templateEditor_usedIfAllEmpty": "Will only be used when the field values above are empty", + "templateEditor_usedIfEmpty": "This field will only be used if the primary field is empty", + "templateEditor_withTime": "Yes, with time. Seperator: ", + "translation_assignedTasks": "Tasks assigned to you", + "translation_assignedTasksHelp": " The list below shows translation tasks assigned to you. To see a detailed view including comments, click on 'Details' or just the page name. You can also download the page as XML directly by clicking the 'Download Xml' link.
    To close a translation task, please go to the Details view and click the 'Close' button. ", + "translation_closeTask": "close task", + "translation_details": "Translation details", + "translation_downloadAllAsXml": "Download all translation tasks as xml", + "translation_downloadTaskAsXml": "Download xml", + "translation_DownloadXmlDTD": "Download xml DTD", + "translation_fields": "Fields", + "translation_includeSubpages": "Include subpages", + "translation_mailBody": " Hi %0% This is an automated mail to inform you that the document '%1%' has been requested for translation into '%5%' by %2%. Go to http://%3%/translation/details.aspx?id=%4% to edit. Or log into umbraco to get an overview of your translation tasks http://%3% Have a nice day! Cheers from the umbraco robot ", + "translation_mailSubject": "[%0%] Translation task for %1%", + "translation_noTranslators": "No translator users found. Please create a translator user before you start sending content to translation", + "translation_ownedTasks": "Tasks created by you", + "translation_ownedTasksHelp": " The list below shows pages created by you. To see a detailed view including comments, click on 'Details' or just the page name. You can also download the page as XML directly by clicking the 'Download Xml' link. To close a translation task, please go to the Details view and click the 'Close' button. ", + "translation_pageHasBeenSendToTranslation": "The page '%0%' has been send to translation", + "translation_sendToTranslate": "Send the page '%0%' to translation", + "translation_taskAssignedBy": "Assigned by", + "translation_taskOpened": "Task opened", + "translation_totalWords": "Total words", + "translation_translateTo": "Translate to", + "translation_translationDone": "Translation completed.", + "translation_translationDoneHelp": "You can preview the pages, you've just translated, by clicking below. If the original page is found, you will get a comparison of the 2 pages.", + "translation_translationFailed": "Translation failed, the xml file might be corrupt", + "translation_translationOptions": "Translation options", + "translation_translator": "Translator", + "translation_uploadTranslationXml": "Upload translation xml", + "treeHeaders_cacheBrowser": "Cache Browser", + "treeHeaders_contentRecycleBin": "Recycle Bin", + "treeHeaders_createdPackages": "Created packages", + "treeHeaders_datatype": "Data Types", + "treeHeaders_dictionary": "Dictionary", + "treeHeaders_installedPackages": "Installed packages", + "treeHeaders_installSkin": "Install skin", + "treeHeaders_installStarterKit": "Install starter kit", + "treeHeaders_languages": "Languages", + "treeHeaders_localPackage": "Install local package", + "treeHeaders_macros": "Macros", + "treeHeaders_mediaTypes": "Media Types", + "treeHeaders_member": "Members", + "treeHeaders_memberGroup": "Member Groups", + "treeHeaders_memberRoles": "Roles", + "treeHeaders_memberType": "Member Types", + "treeHeaders_nodeTypes": "Document Types", + "treeHeaders_packager": "Packages", + "treeHeaders_packages": "Packages", + "treeHeaders_python": "Python Files", + "treeHeaders_repositories": "Install from repository", + "treeHeaders_runway": "Install Runway", + "treeHeaders_runwayModules": "Runway modules", + "treeHeaders_scripting": "Scripting Files", + "treeHeaders_scripts": "Scripts", + "treeHeaders_stylesheets": "Stylesheets", + "treeHeaders_templates": "Templates", + "treeHeaders_xslt": "XSLT Files", + "update_updateAvailable": "New update ready", + "update_updateDownloadText": "%0% is ready, click here for download", + "update_updateNoServer": "No connection to server", + "update_updateNoServerError": "Error checking for update. Please review trace-stack for further information", + "user_administrators": "Administrator", + "user_categoryField": "Category field", + "user_changePassword": "Change Your Password", + "user_newPassword": "Change Your Password", + "user_confirmNewPassword": "Confirm new password", + "user_changePasswordDescription": "You can change your password for accessing the Umbraco Back Office by filling out the form below and click the 'Change Password' button", + "user_contentChannel": "Content Channel", + "user_descriptionField": "Description field", + "user_disabled": "Disable User", + "user_documentType": "Document Type", + "user_editors": "Editor", + "user_excerptField": "Excerpt field", + "user_language": "Language", + "user_loginname": "Login", + "user_mediastartnode": "Start Node in Media Library", + "user_modules": "Sections", + "user_noConsole": "Disable Umbraco Access", + "user_password": "Password", + "user_resetPassword": "Reset password", + "user_passwordChanged": "Your password has been changed!", + "user_passwordConfirm": "Please confirm the new password", + "user_passwordEnterNew": "Enter your new password", + "user_passwordIsBlank": "Your new password cannot be blank!", + "user_passwordCurrent": "Current password", + "user_passwordInvalid": "Invalid current password", + "user_passwordIsDifferent": "There was a difference between the new password and the confirmed password. Please try again!", + "user_passwordMismatch": "The confirmed password doesn't match the new password!", + "user_permissionReplaceChildren": "Replace child node permssions", + "user_permissionSelectedPages": "You are currently modifying permissions for the pages:", + "user_permissionSelectPages": "Select pages to modify their permissions", + "user_searchAllChildren": "Search all children", + "user_startnode": "Start Node in Content", + "user_username": "Username", + "user_userPermissions": "User permissions", + "user_usertype": "User type", + "user_userTypes": "User types", + "user_writer": "Writer", + "user_yourProfile": "Your profile", + "user_yourHistory": "Your recent history", + "user_sessionExpires": "Session expires in" + }, null]; + } + } + + return { + register: function() { + $httpBackend + .whenGET(mocksUtils.urlRegex('js/language.aspx')) + .respond(getLanguageResource); + } + }; }]); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/services/util.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/services/util.mocks.js index 42e5d55a5b..058c6a9d44 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/services/util.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/services/util.mocks.js @@ -1,22 +1,22 @@ -angular.module('umbraco.mocks'). - factory('utilMocks', ['$httpBackend', 'mocksUtils', function ($httpBackend, mocksUtils) { - 'use strict'; - - function getUpdateCheck(status, data, headers) { - //check for existence of a cookie so we can do login/logout in the belle app (ignore for tests). - if (!mocksUtils.checkAuth()) { - return [401, null, null]; - } - else { - return [200, null, null]; - } - } - - return { - register: function() { - $httpBackend - .whenGET(mocksUtils.urlRegex('/umbraco/Api/UpdateCheck/GetCheck')) - .respond(getUpdateCheck); - } - }; +angular.module('umbraco.mocks'). + factory('utilMocks', ['$httpBackend', 'mocksUtils', function ($httpBackend, mocksUtils) { + 'use strict'; + + function getUpdateCheck(status, data, headers) { + //check for existence of a cookie so we can do login/logout in the belle app (ignore for tests). + if (!mocksUtils.checkAuth()) { + return [401, null, null]; + } + else { + return [200, null, null]; + } + } + + return { + register: function() { + $httpBackend + .whenGET(mocksUtils.urlRegex('/umbraco/Api/UpdateCheck/GetCheck')) + .respond(getUpdateCheck); + } + }; }]); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js index 41757f407a..b83ee7edf1 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js @@ -1,107 +1,107 @@ -/** - * @ngdoc service - * @name umbraco.resources.logResource - * @description Retrives log history from umbraco - * - * - **/ -function logResource($q, $http, umbRequestHelper) { - - //the factory object returned - return { - - /** - * @ngdoc method - * @name umbraco.resources.logResource#getEntityLog - * @methodOf umbraco.resources.logResource - * - * @description - * Gets the log history for a give entity id - * - * ##usage - *
    -         * logResource.getEntityLog(1234)
    -         *    .then(function(log) {
    -         *        alert('its here!');
    -         *    });
    -         * 
    - * - * @param {Int} id id of entity to return log history - * @returns {Promise} resourcePromise object containing the log. - * - */ - getEntityLog: function (id) { - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "logApiBaseUrl", - "GetEntityLog", - [{ id: id }])), - 'Failed to retreive user data for id ' + id); - }, - - /** - * @ngdoc method - * @name umbraco.resources.logResource#getUserLog - * @methodOf umbraco.resources.logResource - * - * @description - * Gets the current users' log history for a given type of log entry - * - * ##usage - *
    -         * logResource.getUserLog("save", new Date())
    -         *    .then(function(log) {
    -         *        alert('its here!');
    -         *    });
    -         * 
    - * - * @param {String} type logtype to query for - * @param {DateTime} since query the log back to this date, by defalt 7 days ago - * @returns {Promise} resourcePromise object containing the log. - * - */ - getUserLog: function (type, since) { - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "logApiBaseUrl", - "GetCurrentUserLog", - [{ logtype: type, sinceDate: since }])), - 'Failed to retreive user data for id ' + id); - }, - - /** - * @ngdoc method - * @name umbraco.resources.logResource#getLog - * @methodOf umbraco.resources.logResource - * - * @description - * Gets the log history for a given type of log entry - * - * ##usage - *
    -         * logResource.getLog("save", new Date())
    -         *    .then(function(log) {
    -         *        alert('its here!');
    -         *    });
    -         * 
    - * - * @param {String} type logtype to query for - * @param {DateTime} since query the log back to this date, by defalt 7 days ago - * @returns {Promise} resourcePromise object containing the log. - * - */ - getLog: function (type, since) { - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "logApiBaseUrl", - "GetLog", - [{ logtype: type, sinceDate: since }])), - 'Failed to retreive user data for id ' + id); - } - }; -} - -angular.module('umbraco.resources').factory('logResource', logResource); +/** + * @ngdoc service + * @name umbraco.resources.logResource + * @description Retrives log history from umbraco + * + * + **/ +function logResource($q, $http, umbRequestHelper) { + + //the factory object returned + return { + + /** + * @ngdoc method + * @name umbraco.resources.logResource#getEntityLog + * @methodOf umbraco.resources.logResource + * + * @description + * Gets the log history for a give entity id + * + * ##usage + *
    +         * logResource.getEntityLog(1234)
    +         *    .then(function(log) {
    +         *        alert('its here!');
    +         *    });
    +         * 
    + * + * @param {Int} id id of entity to return log history + * @returns {Promise} resourcePromise object containing the log. + * + */ + getEntityLog: function (id) { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "logApiBaseUrl", + "GetEntityLog", + [{ id: id }])), + 'Failed to retreive user data for id ' + id); + }, + + /** + * @ngdoc method + * @name umbraco.resources.logResource#getUserLog + * @methodOf umbraco.resources.logResource + * + * @description + * Gets the current users' log history for a given type of log entry + * + * ##usage + *
    +         * logResource.getUserLog("save", new Date())
    +         *    .then(function(log) {
    +         *        alert('its here!');
    +         *    });
    +         * 
    + * + * @param {String} type logtype to query for + * @param {DateTime} since query the log back to this date, by defalt 7 days ago + * @returns {Promise} resourcePromise object containing the log. + * + */ + getUserLog: function (type, since) { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "logApiBaseUrl", + "GetCurrentUserLog", + [{ logtype: type, sinceDate: since }])), + 'Failed to retreive user data for id ' + id); + }, + + /** + * @ngdoc method + * @name umbraco.resources.logResource#getLog + * @methodOf umbraco.resources.logResource + * + * @description + * Gets the log history for a given type of log entry + * + * ##usage + *
    +         * logResource.getLog("save", new Date())
    +         *    .then(function(log) {
    +         *        alert('its here!');
    +         *    });
    +         * 
    + * + * @param {String} type logtype to query for + * @param {DateTime} since query the log back to this date, by defalt 7 days ago + * @returns {Promise} resourcePromise object containing the log. + * + */ + getLog: function (type, since) { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "logApiBaseUrl", + "GetLog", + [{ logtype: type, sinceDate: since }])), + 'Failed to retreive user data for id ' + id); + } + }; +} + +angular.module('umbraco.resources').factory('logResource', logResource); diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/macro.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/macro.resource.js index 4e6c03b51a..8ebdfa22fd 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/macro.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/macro.resource.js @@ -1,70 +1,70 @@ -/** - * @ngdoc service - * @name umbraco.resources.macroResource - * @description Deals with data for macros - * - **/ -function macroResource($q, $http, umbRequestHelper) { - - //the factory object returned - return { - - /** - * @ngdoc method - * @name umbraco.resources.macroResource#getMacroParameters - * @methodOf umbraco.resources.macroResource - * - * @description - * Gets the editable macro parameters for the specified macro alias - * - * @param {int} macroId The macro id to get parameters for - * - */ - getMacroParameters: function (macroId) { - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "macroApiBaseUrl", - "GetMacroParameters", - [{ macroId: macroId }])), - 'Failed to retreive macro parameters for macro with id ' + macroId); - }, - - /** - * @ngdoc method - * @name umbraco.resources.macroResource#getMacroResult - * @methodOf umbraco.resources.macroResource - * - * @description - * Gets the result of a macro as html to display in the rich text editor - * - * @param {int} macroId The macro id to get parameters for - * @param {int} pageId The current page id - * @param {Array} macroParamDictionary A dictionary of macro parameters - * - */ - getMacroResultAsHtmlForEditor: function (macroAlias, pageId, macroParamDictionary) { - - //need to format the query string for the custom dictionary - var query = "macroAlias=" + macroAlias + "&pageId=" + pageId; - if (macroParamDictionary) { - var counter = 0; - _.each(macroParamDictionary, function(val, key) { - query += "¯oParams[" + counter + "].key=" + key + "¯oParams[" + counter + "].value=" + val; - counter++; - }); - } - - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "macroApiBaseUrl", - "GetMacroResultAsHtmlForEditor", - query)), - 'Failed to retreive macro result for macro with alias ' + macroAlias); - } - - }; -} - -angular.module('umbraco.resources').factory('macroResource', macroResource); +/** + * @ngdoc service + * @name umbraco.resources.macroResource + * @description Deals with data for macros + * + **/ +function macroResource($q, $http, umbRequestHelper) { + + //the factory object returned + return { + + /** + * @ngdoc method + * @name umbraco.resources.macroResource#getMacroParameters + * @methodOf umbraco.resources.macroResource + * + * @description + * Gets the editable macro parameters for the specified macro alias + * + * @param {int} macroId The macro id to get parameters for + * + */ + getMacroParameters: function (macroId) { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "macroApiBaseUrl", + "GetMacroParameters", + [{ macroId: macroId }])), + 'Failed to retreive macro parameters for macro with id ' + macroId); + }, + + /** + * @ngdoc method + * @name umbraco.resources.macroResource#getMacroResult + * @methodOf umbraco.resources.macroResource + * + * @description + * Gets the result of a macro as html to display in the rich text editor + * + * @param {int} macroId The macro id to get parameters for + * @param {int} pageId The current page id + * @param {Array} macroParamDictionary A dictionary of macro parameters + * + */ + getMacroResultAsHtmlForEditor: function (macroAlias, pageId, macroParamDictionary) { + + //need to format the query string for the custom dictionary + var query = "macroAlias=" + macroAlias + "&pageId=" + pageId; + if (macroParamDictionary) { + var counter = 0; + _.each(macroParamDictionary, function(val, key) { + query += "¯oParams[" + counter + "].key=" + key + "¯oParams[" + counter + "].value=" + val; + counter++; + }); + } + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "macroApiBaseUrl", + "GetMacroResultAsHtmlForEditor", + query)), + 'Failed to retreive macro result for macro with alias ' + macroAlias); + } + + }; +} + +angular.module('umbraco.resources').factory('macroResource', macroResource); diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/member.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/member.resource.js index 3f27e308a5..37d5cdace6 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/member.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/member.resource.js @@ -1,138 +1,138 @@ -/** - * @ngdoc service - * @name umbraco.resources.memberResource - * @description Loads in data for members - **/ -function memberResource($q, $http, umbDataFormatter, umbRequestHelper) { - - /** internal method process the saving of data and post processing the result */ - function saveMember(content, action, files) { - - return umbRequestHelper.postSaveContent({ - restApiUrl: umbRequestHelper.getApiUrl( - "memberApiBaseUrl", - "PostSave"), - content: content, - action: action, - files: files, - dataFormatter: function(c, a) { - return umbDataFormatter.formatMemberPostData(c, a); - } - }); - } - - return { - - - /** - * @ngdoc method - * @name umbraco.resources.memberResource#getByKey - * @methodOf umbraco.resources.memberResource - * - * @description - * Gets a member item with a given key - * - * ##usage - *
    -         * memberResource.getByKey("0000-0000-000-00000-000")
    -         *    .then(function(member) {
    -         *        var mymember = member; 
    -         *        alert('its here!');
    -         *    });
    -         * 
    - * - * @param {Guid} key key of member item to return - * @returns {Promise} resourcePromise object containing the member item. - * - */ - getByKey: function (key) { - - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "memberApiBaseUrl", - "GetByKey", - [{ key: key }])), - 'Failed to retreive data for member id ' + key); - }, - - /** - * @ngdoc method - * @name umbraco.resources.memberResource#deleteByKey - * @methodOf umbraco.resources.memberResource - * - * @description - * Deletes a member item with a given key - * - * ##usage - *
    -         * memberResource.deleteByKey("0000-0000-000-00000-000")
    -         *    .then(function() {
    -         *        alert('its gone!');
    -         *    });
    -         * 
    - * - * @param {Guid} key id of member item to delete - * @returns {Promise} resourcePromise object. - * - */ - deleteByKey: function (key) { - return umbRequestHelper.resourcePromise( - $http.delete( - umbRequestHelper.getApiUrl( - "memberApiBaseUrl", - "DeleteByKey", - [{ key: key }])), - 'Failed to delete item ' + key); - }, - - /** - * @ngdoc method - * @name umbraco.resources.memberResource#getScaffold - * @methodOf umbraco.resources.memberResource - * - * @description - * Returns a scaffold of an empty member item, given the id of the member item to place it underneath and the member type alias. - * - * - Member Type alias must be provided so umbraco knows which properties to put on the member scaffold - * - * The scaffold is used to build editors for member that has not yet been populated with data. - * - * ##usage - *
    -         * memberResource.getScaffold('client')
    -         *    .then(function(scaffold) {
    -         *        var myDoc = scaffold;
    -         *        myDoc.name = "My new member item"; 
    -         *
    -         *        memberResource.save(myDoc, true)
    -         *            .then(function(member){
    -         *                alert("Retrieved, updated and saved again");
    -         *            });
    -         *    });
    -         * 
    - * - * @param {String} alias membertype alias to base the scaffold on - * @returns {Promise} resourcePromise object containing the member scaffold. - * - */ - getScaffold: function (alias) { - - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "memberApiBaseUrl", - "GetEmpty", - [{ contentTypeAlias: alias }])), - 'Failed to retreive data for empty member item type ' + alias); - - }, - - /** saves or updates a member object */ - save: function (member, isNew, files) { - return saveMember(member, "save" + (isNew ? "New" : ""), files); - } - }; -} - -angular.module('umbraco.resources').factory('memberResource', memberResource); +/** + * @ngdoc service + * @name umbraco.resources.memberResource + * @description Loads in data for members + **/ +function memberResource($q, $http, umbDataFormatter, umbRequestHelper) { + + /** internal method process the saving of data and post processing the result */ + function saveMember(content, action, files) { + + return umbRequestHelper.postSaveContent({ + restApiUrl: umbRequestHelper.getApiUrl( + "memberApiBaseUrl", + "PostSave"), + content: content, + action: action, + files: files, + dataFormatter: function(c, a) { + return umbDataFormatter.formatMemberPostData(c, a); + } + }); + } + + return { + + + /** + * @ngdoc method + * @name umbraco.resources.memberResource#getByKey + * @methodOf umbraco.resources.memberResource + * + * @description + * Gets a member item with a given key + * + * ##usage + *
    +         * memberResource.getByKey("0000-0000-000-00000-000")
    +         *    .then(function(member) {
    +         *        var mymember = member; 
    +         *        alert('its here!');
    +         *    });
    +         * 
    + * + * @param {Guid} key key of member item to return + * @returns {Promise} resourcePromise object containing the member item. + * + */ + getByKey: function (key) { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "memberApiBaseUrl", + "GetByKey", + [{ key: key }])), + 'Failed to retreive data for member id ' + key); + }, + + /** + * @ngdoc method + * @name umbraco.resources.memberResource#deleteByKey + * @methodOf umbraco.resources.memberResource + * + * @description + * Deletes a member item with a given key + * + * ##usage + *
    +         * memberResource.deleteByKey("0000-0000-000-00000-000")
    +         *    .then(function() {
    +         *        alert('its gone!');
    +         *    });
    +         * 
    + * + * @param {Guid} key id of member item to delete + * @returns {Promise} resourcePromise object. + * + */ + deleteByKey: function (key) { + return umbRequestHelper.resourcePromise( + $http.delete( + umbRequestHelper.getApiUrl( + "memberApiBaseUrl", + "DeleteByKey", + [{ key: key }])), + 'Failed to delete item ' + key); + }, + + /** + * @ngdoc method + * @name umbraco.resources.memberResource#getScaffold + * @methodOf umbraco.resources.memberResource + * + * @description + * Returns a scaffold of an empty member item, given the id of the member item to place it underneath and the member type alias. + * + * - Member Type alias must be provided so umbraco knows which properties to put on the member scaffold + * + * The scaffold is used to build editors for member that has not yet been populated with data. + * + * ##usage + *
    +         * memberResource.getScaffold('client')
    +         *    .then(function(scaffold) {
    +         *        var myDoc = scaffold;
    +         *        myDoc.name = "My new member item"; 
    +         *
    +         *        memberResource.save(myDoc, true)
    +         *            .then(function(member){
    +         *                alert("Retrieved, updated and saved again");
    +         *            });
    +         *    });
    +         * 
    + * + * @param {String} alias membertype alias to base the scaffold on + * @returns {Promise} resourcePromise object containing the member scaffold. + * + */ + getScaffold: function (alias) { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "memberApiBaseUrl", + "GetEmpty", + [{ contentTypeAlias: alias }])), + 'Failed to retreive data for empty member item type ' + alias); + + }, + + /** saves or updates a member object */ + save: function (member, isNew, files) { + return saveMember(member, "save" + (isNew ? "New" : ""), files); + } + }; +} + +angular.module('umbraco.resources').factory('memberResource', memberResource); diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js index 55bfc1ede5..87ba4f2ee3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js @@ -1,23 +1,23 @@ -/** - * @ngdoc service - * @name umbraco.resources.memberTypeResource - * @description Loads in data for member types - **/ -function memberTypeResource($q, $http, umbRequestHelper) { - - return { - - //return all member types - getTypes: function () { - - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "memberTypeApiBaseUrl", - "GetAllTypes")), - 'Failed to retreive data for member types id'); - } - - }; -} +/** + * @ngdoc service + * @name umbraco.resources.memberTypeResource + * @description Loads in data for member types + **/ +function memberTypeResource($q, $http, umbRequestHelper) { + + return { + + //return all member types + getTypes: function () { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "memberTypeApiBaseUrl", + "GetAllTypes")), + 'Failed to retreive data for member types id'); + } + + }; +} angular.module('umbraco.resources').factory('memberTypeResource', memberTypeResource); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js index 0e5fdc3916..057e0b8cff 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js @@ -1,195 +1,195 @@ -/** - * @ngdoc service - * @name umbraco.services.formHelper - * @function - * - * @description - * A utility class used to streamline how forms are developed, to ensure that validation is check and displayed consistently and to ensure that the correct events - * fire when they need to. - */ -function formHelper(angularHelper, serverValidationManager, $timeout, notificationsService, dialogService) { - return { - - /** - * @ngdoc function - * @name umbraco.services.formHelper#submitForm - * @methodOf umbraco.services.formHelper - * @function - * - * @description - * Called by controllers when submitting a form - this ensures that all client validation is checked, - * server validation is cleared, that the correct events execute and status messages are displayed. - * This returns true if the form is valid, otherwise false if form submission cannot continue. - * - * @param {object} args An object containing arguments for form submission - */ - submitForm: function (args) { - - var currentForm; - - if (!args) { - throw "args cannot be null"; - } - if (!args.scope) { - throw "args.scope cannot be null"; - } - if (!args.formCtrl) { - //try to get the closest form controller - currentForm = angularHelper.getRequiredCurrentForm(args.scope); - } - else { - currentForm = args.formCtrl; - } - //if no statusPropertyName is set we'll default to formStatus. - if (!args.statusPropertyName) { - args.statusPropertyName = "formStatus"; - } - //if no statusTimeout is set, we'll default to 2500 ms - if (!args.statusTimeout) { - args.statusTimeout = 2500; - } - - //the first thing any form must do is broadcast the formSubmitting event - args.scope.$broadcast("formSubmitting", { scope: args.scope }); - - //then check if the form is valid - if (!args.skipValidation) { - if (currentForm.$invalid) { - return false; - } - } - - //reset the server validations - serverValidationManager.reset(); - - //check if a form status should be set on the scope - if (args.statusMessage) { - args.scope[args.statusPropertyName] = args.statusMessage; - - //clear the message after the timeout - $timeout(function () { - args.scope[args.statusPropertyName] = undefined; - }, args.statusTimeout); - } - - return true; - }, - - /** - * @ngdoc function - * @name umbraco.services.formHelper#submitForm - * @methodOf umbraco.services.formHelper - * @function - * - * @description - * Called by controllers when a form has been successfully submitted. the correct events execute - * and that the notifications are displayed if there are any. - * - * @param {object} args An object containing arguments for form submission - */ - resetForm: function (args) { - if (!args) { - throw "args cannot be null"; - } - if (!args.scope) { - throw "args.scope cannot be null"; - } - - //if no statusPropertyName is set we'll default to formStatus. - if (!args.statusPropertyName) { - args.statusPropertyName = "formStatus"; - } - //clear the status - args.scope[args.statusPropertyName] = null; - - if (angular.isArray(args.notifications)) { - for (var i = 0; i < args.notifications.length; i++) { - notificationsService.showNotification(args.notifications[i]); - } - } - - args.scope.$broadcast("formSubmitted", { scope: args.scope }); - }, - - /** - * @ngdoc function - * @name umbraco.services.formHelper#handleError - * @methodOf umbraco.services.formHelper - * @function - * - * @description - * Needs to be called when a form submission fails, this will wire up all server validation errors in ModelState and - * add the correct messages to the notifications. If a server error has occurred this will show a ysod. - * - * @param {object} err The error object returned from the http promise - */ - handleError: function (err) { - //When the status is a 400 status with a custom header: X-Status-Reason: Validation failed, we have validation errors. - //Otherwise the error is probably due to invalid data (i.e. someone mucking around with the ids or something). - //Or, some strange server error - if (err.status === 400) { - //now we need to look through all the validation errors - if (err.data && (err.data.ModelState)) { - - //wire up the server validation errs - this.handleServerValidation(err.data.ModelState); - - //execute all server validation events and subscribers - serverValidationManager.executeAndClearAllSubscriptions(); - } - else { - dialogService.ysodDialog(err); - } - } - else { - dialogService.ysodDialog(err); - } - }, - - /** - * @ngdoc function - * @name umbraco.services.formHelper#handleServerValidation - * @methodOf umbraco.services.formHelper - * @function - * - * @description - * This wires up all of the server validation model state so that valServer and valServerField directives work - * - * @param {object} err The error object returned from the http promise - */ - handleServerValidation: function(modelState) { - for (var e in modelState) { - - //the alias in model state can be in dot notation which indicates - // * the first part is the content property alias - // * the second part is the field to which the valiation msg is associated with - //There will always be at least 2 parts for properties since all model errors for properties are prefixed with "Properties" - //If it is not prefixed with "Properties" that means the error is for a field of the object directly. - - var parts = e.split("."); - if (parts.length > 1) { - var propertyAlias = parts[1]; - - //if it contains 2 '.' then we will wire it up to a property's field - if (parts.length > 2) { - //add an error with a reference to the field for which the validation belongs too - serverValidationManager.addPropertyError(propertyAlias, parts[2], modelState[e][0]); - } - else { - //add a generic error for the property, no reference to a specific field - serverValidationManager.addPropertyError(propertyAlias, "", modelState[e][0]); - } - - } - else { - //the parts are only 1, this means its not a property but a native content property - serverValidationManager.addFieldError(parts[0], modelState[e][0]); - } - - //add to notifications - notificationsService.error("Validation", modelState[e][0]); - } - } - }; -} +/** + * @ngdoc service + * @name umbraco.services.formHelper + * @function + * + * @description + * A utility class used to streamline how forms are developed, to ensure that validation is check and displayed consistently and to ensure that the correct events + * fire when they need to. + */ +function formHelper(angularHelper, serverValidationManager, $timeout, notificationsService, dialogService) { + return { + + /** + * @ngdoc function + * @name umbraco.services.formHelper#submitForm + * @methodOf umbraco.services.formHelper + * @function + * + * @description + * Called by controllers when submitting a form - this ensures that all client validation is checked, + * server validation is cleared, that the correct events execute and status messages are displayed. + * This returns true if the form is valid, otherwise false if form submission cannot continue. + * + * @param {object} args An object containing arguments for form submission + */ + submitForm: function (args) { + + var currentForm; + + if (!args) { + throw "args cannot be null"; + } + if (!args.scope) { + throw "args.scope cannot be null"; + } + if (!args.formCtrl) { + //try to get the closest form controller + currentForm = angularHelper.getRequiredCurrentForm(args.scope); + } + else { + currentForm = args.formCtrl; + } + //if no statusPropertyName is set we'll default to formStatus. + if (!args.statusPropertyName) { + args.statusPropertyName = "formStatus"; + } + //if no statusTimeout is set, we'll default to 2500 ms + if (!args.statusTimeout) { + args.statusTimeout = 2500; + } + + //the first thing any form must do is broadcast the formSubmitting event + args.scope.$broadcast("formSubmitting", { scope: args.scope }); + + //then check if the form is valid + if (!args.skipValidation) { + if (currentForm.$invalid) { + return false; + } + } + + //reset the server validations + serverValidationManager.reset(); + + //check if a form status should be set on the scope + if (args.statusMessage) { + args.scope[args.statusPropertyName] = args.statusMessage; + + //clear the message after the timeout + $timeout(function () { + args.scope[args.statusPropertyName] = undefined; + }, args.statusTimeout); + } + + return true; + }, + + /** + * @ngdoc function + * @name umbraco.services.formHelper#submitForm + * @methodOf umbraco.services.formHelper + * @function + * + * @description + * Called by controllers when a form has been successfully submitted. the correct events execute + * and that the notifications are displayed if there are any. + * + * @param {object} args An object containing arguments for form submission + */ + resetForm: function (args) { + if (!args) { + throw "args cannot be null"; + } + if (!args.scope) { + throw "args.scope cannot be null"; + } + + //if no statusPropertyName is set we'll default to formStatus. + if (!args.statusPropertyName) { + args.statusPropertyName = "formStatus"; + } + //clear the status + args.scope[args.statusPropertyName] = null; + + if (angular.isArray(args.notifications)) { + for (var i = 0; i < args.notifications.length; i++) { + notificationsService.showNotification(args.notifications[i]); + } + } + + args.scope.$broadcast("formSubmitted", { scope: args.scope }); + }, + + /** + * @ngdoc function + * @name umbraco.services.formHelper#handleError + * @methodOf umbraco.services.formHelper + * @function + * + * @description + * Needs to be called when a form submission fails, this will wire up all server validation errors in ModelState and + * add the correct messages to the notifications. If a server error has occurred this will show a ysod. + * + * @param {object} err The error object returned from the http promise + */ + handleError: function (err) { + //When the status is a 400 status with a custom header: X-Status-Reason: Validation failed, we have validation errors. + //Otherwise the error is probably due to invalid data (i.e. someone mucking around with the ids or something). + //Or, some strange server error + if (err.status === 400) { + //now we need to look through all the validation errors + if (err.data && (err.data.ModelState)) { + + //wire up the server validation errs + this.handleServerValidation(err.data.ModelState); + + //execute all server validation events and subscribers + serverValidationManager.executeAndClearAllSubscriptions(); + } + else { + dialogService.ysodDialog(err); + } + } + else { + dialogService.ysodDialog(err); + } + }, + + /** + * @ngdoc function + * @name umbraco.services.formHelper#handleServerValidation + * @methodOf umbraco.services.formHelper + * @function + * + * @description + * This wires up all of the server validation model state so that valServer and valServerField directives work + * + * @param {object} err The error object returned from the http promise + */ + handleServerValidation: function(modelState) { + for (var e in modelState) { + + //the alias in model state can be in dot notation which indicates + // * the first part is the content property alias + // * the second part is the field to which the valiation msg is associated with + //There will always be at least 2 parts for properties since all model errors for properties are prefixed with "Properties" + //If it is not prefixed with "Properties" that means the error is for a field of the object directly. + + var parts = e.split("."); + if (parts.length > 1) { + var propertyAlias = parts[1]; + + //if it contains 2 '.' then we will wire it up to a property's field + if (parts.length > 2) { + //add an error with a reference to the field for which the validation belongs too + serverValidationManager.addPropertyError(propertyAlias, parts[2], modelState[e][0]); + } + else { + //add a generic error for the property, no reference to a specific field + serverValidationManager.addPropertyError(propertyAlias, "", modelState[e][0]); + } + + } + else { + //the parts are only 1, this means its not a property but a native content property + serverValidationManager.addFieldError(parts[0], modelState[e][0]); + } + + //add to notifications + notificationsService.error("Validation", modelState[e][0]); + } + } + }; +} angular.module('umbraco.services').factory('formHelper', formHelper); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/services/localization.service.js b/src/Umbraco.Web.UI.Client/src/common/services/localization.service.js index 64d4e785a0..70e4b9cba3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/localization.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/localization.service.js @@ -1,114 +1,114 @@ -angular.module('umbraco.services') -.factory('localizationService', function ($http, $q, $rootScope, $window, $filter, userService) { - var service = { - // array to hold the localized resource string entries - dictionary:[], - // location of the resource file - url: "js/language.aspx", - // flag to indicate if the service hs loaded the resource file - resourceFileLoaded:false, - - // success handler for all server communication - successCallback:function (data) { - // store the returned array in the dictionary - service.dictionary = data; - // set the flag that the resource are loaded - service.resourceFileLoaded = true; - // broadcast that the file has been loaded - $rootScope.$broadcast('localizeResourcesUpdates'); - }, - - // allows setting of language on the fly - setLanguage: function(value) { - service.initLocalizedResources(); - }, - - // allows setting of resource url on the fly - setUrl: function(value) { - service.url = value; - service.initLocalizedResources(); - }, - - // loads the language resource file from the server - initLocalizedResources:function () { - var deferred = $q.defer(); - // build the url to retrieve the localized resource file - $http({ method:"GET", url:service.url, cache:false }) - .then(function(response){ - service.resourceFileLoaded = true; - service.dictionary = response.data; - - $rootScope.$broadcast('localizeResourcesUpdates'); - - return deferred.resolve(service.dictionary); - }, function(err){ - return deferred.reject("Something broke"); - }); - return deferred.promise; - }, - - //helper to tokenize and compile a localization string - tokenize: function(value,scope) { - if(value){ - var localizer = value.split(':'); - var retval = {tokens: undefined, key: localizer[0].substring(0)}; - if(localizer.length > 1){ - retval.tokens = localizer[1].split(','); - for (var x = 0; x < retval.tokens.length; x++) { - retval.tokens[x] = scope.$eval(retval.tokens[x]); - } - } - - return retval; - } - }, - - // checks the dictionary for a localized resource string - localize: function(value,tokens) { - var deferred = $q.defer(); - - if(service.resourceFileLoaded){ - var val = service._lookup(value,tokens); - deferred.resolve(val); - }else{ - service.initLocalizedResources().then(function(dic){ - var val = service._lookup(value,tokens); - deferred.resolve(val); - }); - } - - return deferred.promise; - }, - _lookup: function(value,tokens){ - - //strip the key identifier if its there - if(value && value[0] === "@"){ - value = value.substring(1); - } - - //if no area specified, add general_ - if(value && value.indexOf("_") < 0){ - value = "general_" + value; - } - - var entry = service.dictionary[value]; - if(entry){ - if(tokens){ - for (var i = 0; i < tokens.length; i++) { - entry = entry.replace("%"+i+"%", tokens[i]); - } - } - return entry; - } - return "[" + value + "]"; - } - - - }; - - // force the load of the resource file - service.initLocalizedResources(); - - // return the local instance when called - return service; +angular.module('umbraco.services') +.factory('localizationService', function ($http, $q, $rootScope, $window, $filter, userService) { + var service = { + // array to hold the localized resource string entries + dictionary:[], + // location of the resource file + url: "js/language.aspx", + // flag to indicate if the service hs loaded the resource file + resourceFileLoaded:false, + + // success handler for all server communication + successCallback:function (data) { + // store the returned array in the dictionary + service.dictionary = data; + // set the flag that the resource are loaded + service.resourceFileLoaded = true; + // broadcast that the file has been loaded + $rootScope.$broadcast('localizeResourcesUpdates'); + }, + + // allows setting of language on the fly + setLanguage: function(value) { + service.initLocalizedResources(); + }, + + // allows setting of resource url on the fly + setUrl: function(value) { + service.url = value; + service.initLocalizedResources(); + }, + + // loads the language resource file from the server + initLocalizedResources:function () { + var deferred = $q.defer(); + // build the url to retrieve the localized resource file + $http({ method:"GET", url:service.url, cache:false }) + .then(function(response){ + service.resourceFileLoaded = true; + service.dictionary = response.data; + + $rootScope.$broadcast('localizeResourcesUpdates'); + + return deferred.resolve(service.dictionary); + }, function(err){ + return deferred.reject("Something broke"); + }); + return deferred.promise; + }, + + //helper to tokenize and compile a localization string + tokenize: function(value,scope) { + if(value){ + var localizer = value.split(':'); + var retval = {tokens: undefined, key: localizer[0].substring(0)}; + if(localizer.length > 1){ + retval.tokens = localizer[1].split(','); + for (var x = 0; x < retval.tokens.length; x++) { + retval.tokens[x] = scope.$eval(retval.tokens[x]); + } + } + + return retval; + } + }, + + // checks the dictionary for a localized resource string + localize: function(value,tokens) { + var deferred = $q.defer(); + + if(service.resourceFileLoaded){ + var val = service._lookup(value,tokens); + deferred.resolve(val); + }else{ + service.initLocalizedResources().then(function(dic){ + var val = service._lookup(value,tokens); + deferred.resolve(val); + }); + } + + return deferred.promise; + }, + _lookup: function(value,tokens){ + + //strip the key identifier if its there + if(value && value[0] === "@"){ + value = value.substring(1); + } + + //if no area specified, add general_ + if(value && value.indexOf("_") < 0){ + value = "general_" + value; + } + + var entry = service.dictionary[value]; + if(entry){ + if(tokens){ + for (var i = 0; i < tokens.length; i++) { + entry = entry.replace("%"+i+"%", tokens[i]); + } + } + return entry; + } + return "[" + value + "]"; + } + + + }; + + // force the load of the resource file + service.initLocalizedResources(); + + // return the local instance when called + return service; }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js b/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js index ccb9c5eeb2..c0efdae67a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js @@ -1,145 +1,145 @@ -/** - * @ngdoc service - * @name umbraco.services.macroService - * - * - * @description - * A service to return macro information such as generating syntax to insert a macro into an editor - */ -function macroService() { - - return { - - /** parses the special macro syntax like and returns an object with the macro alias and it's parameters */ - parseMacroSyntax: function (syntax) { - - var expression = /(<\?UMBRACO_MACRO macroAlias=["'](\w+?)["'].+?)(\/>|>.*?<\/\?UMBRACO_MACRO>)/im; - var match = expression.exec(syntax); - if (!match || match.length < 3) { - return null; - } - var alias = match[2]; - - //this will leave us with just the parameters - var paramsChunk = match[1].trim().replace(new RegExp("UMBRACO_MACRO macroAlias=[\"']" + alias + "[\"']"), "").trim(); - - var paramExpression = new RegExp("(\\w+?)=['\"](.*?)['\"]", "g"); - var paramMatch; - var returnVal = { - macroAlias: alias, - marcoParamsDictionary: {} - }; - while (paramMatch = paramExpression.exec(paramsChunk)) { - returnVal.marcoParamsDictionary[paramMatch[1]] = paramMatch[2]; - } - return returnVal; - }, - - /** - * @ngdoc function - * @name umbraco.services.macroService#generateWebFormsSyntax - * @methodOf umbraco.services.macroService - * @function - * - * @description - * generates the syntax for inserting a macro into a rich text editor - this is the very old umbraco style syntax - * - * @param {object} args an object containing the macro alias and it's parameter values - */ - generateMacroSyntax: function (args) { - - // - - var macroString = '"; - - return macroString; - }, - - /** - * @ngdoc function - * @name umbraco.services.macroService#generateWebFormsSyntax - * @methodOf umbraco.services.macroService - * @function - * - * @description - * generates the syntax for inserting a macro into a webforms templates - * - * @param {object} args an object containing the macro alias and it's parameter values - */ - generateWebFormsSyntax: function(args) { - - var macroString = '"; - - return macroString; - }, - - /** - * @ngdoc function - * @name umbraco.services.macroService#generateMvcSyntax - * @methodOf umbraco.services.macroService - * @function - * - * @description - * generates the syntax for inserting a macro into an mvc template - * - * @param {object} args an object containing the macro alias and it's parameter values - */ - generateMvcSyntax: function (args) { - - var macroString = "@Umbraco.RenderMacro(\"" + args.macroAlias + "\""; - - var hasParams = false; - var paramString; - if (args.marcoParamsDictionary) { - - paramString = ", new {"; - - _.each(args.marcoParamsDictionary, function(val, key) { - - hasParams = true; - - var keyVal = key + "=\"" + (val ? val : "") + "\", "; - - paramString += keyVal; - }); - - //remove the last , - paramString = paramString.trimEnd(", "); - - paramString += "}"; - } - if (hasParams) { - macroString += paramString; - } - - macroString += ")"; - return macroString; - } - - }; - -} - +/** + * @ngdoc service + * @name umbraco.services.macroService + * + * + * @description + * A service to return macro information such as generating syntax to insert a macro into an editor + */ +function macroService() { + + return { + + /** parses the special macro syntax like and returns an object with the macro alias and it's parameters */ + parseMacroSyntax: function (syntax) { + + var expression = /(<\?UMBRACO_MACRO macroAlias=["'](\w+?)["'].+?)(\/>|>.*?<\/\?UMBRACO_MACRO>)/im; + var match = expression.exec(syntax); + if (!match || match.length < 3) { + return null; + } + var alias = match[2]; + + //this will leave us with just the parameters + var paramsChunk = match[1].trim().replace(new RegExp("UMBRACO_MACRO macroAlias=[\"']" + alias + "[\"']"), "").trim(); + + var paramExpression = new RegExp("(\\w+?)=['\"](.*?)['\"]", "g"); + var paramMatch; + var returnVal = { + macroAlias: alias, + marcoParamsDictionary: {} + }; + while (paramMatch = paramExpression.exec(paramsChunk)) { + returnVal.marcoParamsDictionary[paramMatch[1]] = paramMatch[2]; + } + return returnVal; + }, + + /** + * @ngdoc function + * @name umbraco.services.macroService#generateWebFormsSyntax + * @methodOf umbraco.services.macroService + * @function + * + * @description + * generates the syntax for inserting a macro into a rich text editor - this is the very old umbraco style syntax + * + * @param {object} args an object containing the macro alias and it's parameter values + */ + generateMacroSyntax: function (args) { + + // + + var macroString = '"; + + return macroString; + }, + + /** + * @ngdoc function + * @name umbraco.services.macroService#generateWebFormsSyntax + * @methodOf umbraco.services.macroService + * @function + * + * @description + * generates the syntax for inserting a macro into a webforms templates + * + * @param {object} args an object containing the macro alias and it's parameter values + */ + generateWebFormsSyntax: function(args) { + + var macroString = '"; + + return macroString; + }, + + /** + * @ngdoc function + * @name umbraco.services.macroService#generateMvcSyntax + * @methodOf umbraco.services.macroService + * @function + * + * @description + * generates the syntax for inserting a macro into an mvc template + * + * @param {object} args an object containing the macro alias and it's parameter values + */ + generateMvcSyntax: function (args) { + + var macroString = "@Umbraco.RenderMacro(\"" + args.macroAlias + "\""; + + var hasParams = false; + var paramString; + if (args.marcoParamsDictionary) { + + paramString = ", new {"; + + _.each(args.marcoParamsDictionary, function(val, key) { + + hasParams = true; + + var keyVal = key + "=\"" + (val ? val : "") + "\", "; + + paramString += keyVal; + }); + + //remove the last , + paramString = paramString.trimEnd(", "); + + paramString += "}"; + } + if (hasParams) { + macroString += paramString; + } + + macroString += ")"; + return macroString; + } + + }; + +} + angular.module('umbraco.services').factory('macroService', macroService); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js index 538d9e6e50..c41bd45f14 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js @@ -1,504 +1,504 @@ -/** - * @ngdoc service - * @name umbraco.services.tinyMceService - * - * - * @description - * A service containing all logic for all of the Umbraco TinyMCE plugins - */ -function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macroResource, macroService, $routeParams, umbRequestHelper) { - return { - - /** - * @ngdoc method - * @name umbraco.services.tinyMceService#configuration - * @methodOf umbraco.services.tinyMceService - * - * @description - * Returns a collection of plugins available to the tinyMCE editor - * - */ - configuration: function () { - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "rteApiBaseUrl", - "GetConfiguration")), - 'Failed to retreive entity data for id '); - }, - - /** - * @ngdoc method - * @name umbraco.services.tinyMceService#defaultPrevalues - * @methodOf umbraco.services.tinyMceService - * - * @description - * Returns a default configration to fallback on in case none is provided - * - */ - defaultPrevalues: function () { - var cfg = {}; - cfg.toolbar = ["code", "bold", "italic", "umbracocss","alignleft", "aligncenter", "alignright", "bullist","numlist", "outdent", "indent", "link", "image", "umbmediapicker", "umbembeddialog", "umbmacro"]; - cfg.stylesheets = []; - cfg.dimensions = {height: 400, width: 600}; - return cfg; - }, - - /** - * @ngdoc method - * @name umbraco.services.tinyMceService#createInsertEmbeddedMedia - * @methodOf umbraco.services.tinyMceService - * - * @description - * Creates the umbrco insert embedded media tinymce plugin - * - * @param {Object} editor the TinyMCE editor instance - * @param {Object} $scope the current controller scope - */ - createInsertEmbeddedMedia: function (editor, $scope) { - editor.addButton('umbembeddialog', { - icon: 'custom icon-tv', - tooltip: 'Embed', - onclick: function () { - dialogService.embedDialog({ - scope: $scope, callback: function (data) { - editor.insertContent(data); - } - }); - } - }); - }, - - /** - * @ngdoc method - * @name umbraco.services.tinyMceService#createMediaPicker - * @methodOf umbraco.services.tinyMceService - * - * @description - * Creates the umbrco insert media tinymce plugin - * - * @param {Object} editor the TinyMCE editor instance - * @param {Object} $scope the current controller scope - */ - createMediaPicker: function (editor, $scope) { - editor.addButton('umbmediapicker', { - icon: 'custom icon-picture', - tooltip: 'Media Picker', - onclick: function () { - dialogService.mediaPicker({ - scope: $scope, callback: function (img) { - - if (img) { - var imagePropVal = imageHelper.getImagePropertyValue({ imageModel: img, scope: $scope }); - var data = { - alt: "Some description", - src: (imagePropVal) ? imagePropVal : "nothing.jpg", - id: '__mcenew' - }; - - - editor.insertContent(editor.dom.createHTML('img', data)); - - $timeout(function () { - var imgElm = editor.dom.get('__mcenew'); - var size = editor.dom.getSize(imgElm); - - var newSize = imageHelper.scaleToMaxSize(500, size.w, size.h); - var s = "width: " + newSize.width + "px; height:" + newSize.height + "px;"; - editor.dom.setAttrib(imgElm, 'style', s); - editor.dom.setAttrib(imgElm, 'rel', newSize.width + "," + newSize.height); - editor.dom.setAttrib(imgElm, 'id', null); - - }, 500); - } - } - }); - } - }); - }, - - /** - * @ngdoc method - * @name umbraco.services.tinyMceService#createLinkPicker - * @methodOf umbraco.services.tinyMceService - * - * @description - * Creates the umbrco insert link tinymce plugin - * - * @param {Object} editor the TinyMCE editor instance - * @param {Object} $scope the current controller scope - */ - createLinkPicker: function (editor, $scope) { - - - - /* - editor.addButton('link', { - icon: 'custom icon-link', - tooltip: 'Link Picker', - onclick: function () { - dialogService.linkPicker({ - scope: $scope, callback: function (link) { - if (link) { - var data = { - title: "Some description", - href: "", - id: '__mcenew' - }; - - editor.execCommand("mceInsertLink", false, { - href: "wat", - title: "muh", - target: null, - "class": null - }); - - - //editor.insertContent(editor.dom.createHTML('a', data)); - } - } - }); - } - });*/ - }, - - /** - * @ngdoc method - * @name umbraco.services.tinyMceService#createUmbracoMacro - * @methodOf umbraco.services.tinyMceService - * - * @description - * Creates the insert umbrco macro tinymce plugin - * - * @param {Object} editor the TinyMCE editor instance - * @param {Object} $scope the current controller scope - */ - createInsertMacro: function (editor, $scope) { - - /** Adds custom rules for the macro plugin and custom serialization */ - editor.on('preInit', function (args) { - //this is requires so that we tell the serializer that a 'div' is actually allowed in the root, otherwise the cleanup will strip it out - editor.serializer.addRules('div'); - - /** This checks if the div is a macro container, if so, checks if its wrapped in a p tag and then unwraps it (removes p tag) */ - editor.serializer.addNodeFilter('div', function (nodes, name) { - for (var i = 0; i < nodes.length; i++) { - if (nodes[i].attr("class") === "umb-macro-holder" && nodes[i].parent && nodes[i].parent.name.toUpperCase() === "P") { - nodes[i].parent.unwrap(); - } - } - }); - - }); - - /** - * Because the macro gets wrapped in a P tag because of the way 'enter' works, this - * method will return the macro element if not wrapped in a p, or the p if the macro - * element is the only one inside of it even if we are deep inside an element inside the macro - */ - function getRealMacroElem(element) { - var e = $(element).closest(".umb-macro-holder"); - if (e.length > 0) { - if (e.get(0).parentNode.nodeName === "P") { - //now check if we're the only element - if (element.parentNode.childNodes.length === 1) { - return e.get(0).parentNode; - } - } - return e.get(0); - } - return null; - } - - /** loads in the macro content async from the server */ - function loadMacroContent($macroDiv, macroData) { - - //if we don't have the macroData, then we'll need to parse it from the macro div - if (!macroData) { - var contents = $macroDiv.contents(); - var comment = _.find(contents, function (item) { - return item.nodeType === 8; - }); - if (!comment) { - throw "Cannot parse the current macro, the syntax in the editor is invalid"; - } - var syntax = comment.textContent.trim(); - var parsed = macroService.parseMacroSyntax(syntax); - macroData = parsed; - } - - var $ins = $macroDiv.find("ins"); - - //show the throbber - $macroDiv.addClass("loading"); - - var contentId = $routeParams.id; - - macroResource.getMacroResultAsHtmlForEditor(macroData.macroAlias, contentId, macroData.marcoParamsDictionary) - .then(function (htmlResult) { - - $macroDiv.removeClass("loading"); - htmlResult = htmlResult.trim(); - if (htmlResult !== "") { - $ins.html(htmlResult); - } - }); - } - - /** Adds the button instance */ - editor.addButton('umbmacro', { - icon: 'custom icon-settings-alt', - tooltip: 'Insert macro', - onPostRender: function () { - - var ctrl = this; - var isOnMacroElement = false; - - /** - if the selection comes from a different element that is not the macro's - we need to check if the selection includes part of the macro, if so we'll force the selection - to clear to the next element since if people can select part of the macro markup they can then modify it. - */ - function handleSelectionChange() { - - if (!editor.selection.isCollapsed()) { - var endSelection = tinymce.activeEditor.selection.getEnd(); - var startSelection = tinymce.activeEditor.selection.getStart(); - //don't proceed if it's an entire element selected - if (endSelection !== startSelection) { - - //if the end selection is a macro then move the cursor - //NOTE: we don't have to handle when the selection comes from a previous parent because - // that is automatically taken care of with the normal onNodeChanged logic since the - // evt.element will be the macro once it becomes part of the selection. - var $testForMacro = $(endSelection).closest(".umb-macro-holder"); - if ($testForMacro.length > 0) { - - //it came from before so move after, if there is no after then select ourselves - var next = $testForMacro.next(); - if (next.length > 0) { - editor.selection.setCursorLocation($testForMacro.next().get(0)); - } - else { - selectMacroElement($testForMacro.get(0)); - } - - } - } - } - } - - /** helper method to select the macro element */ - function selectMacroElement(macroElement) { - // move selection to top element to ensure we can't edit this - editor.selection.select(macroElement); - - // check if the current selection *is* the element (ie bug) - var currentSelection = editor.selection.getStart(); - if (tinymce.isIE) { - if (!editor.dom.hasClass(currentSelection, 'umb-macro-holder')) { - while (!editor.dom.hasClass(currentSelection, 'umb-macro-holder') && currentSelection.parentNode) { - currentSelection = currentSelection.parentNode; - } - editor.selection.select(currentSelection); - } - } - } - - /** - * Add a node change handler, test if we're editing a macro and select the whole thing, then set our isOnMacroElement flag. - * If we change the selection inside this method, then we end up in an infinite loop, so we have to remove ourselves - * from the event listener before changing selection, however, it seems that putting a break point in this method - * will always cause an 'infinite' loop as the caret keeps changing. - */ - function onNodeChanged(evt) { - - //set our macro button active when on a node of class umb-macro-holder - var $macroElement = $(evt.element).closest(".umb-macro-holder"); - - handleSelectionChange(); - - //set the button active - ctrl.active($macroElement.length !== 0); - - if ($macroElement.length > 0) { - var macroElement = $macroElement.get(0); - - //remove the event listener before re-selecting - editor.off('NodeChange', onNodeChanged); - - selectMacroElement(macroElement); - - //set the flag - isOnMacroElement = true; - - //re-add the event listener - editor.on('NodeChange', onNodeChanged); - } - else { - isOnMacroElement = false; - } - - } - - /** when the contents load we need to find any macros declared and load in their content */ - editor.on("LoadContent", function (o) { - - //get all macro divs and load their content - $(editor.dom.select(".umb-macro-holder.mceNonEditable")).each(function() { - loadMacroContent($(this)); - }); - - }); - - /** This prevents any other commands from executing when the current element is the macro so the content cannot be edited */ - editor.on('BeforeExecCommand', function (o) { - if (isOnMacroElement) { - if (o.preventDefault) { - o.preventDefault(); - } - if (o.stopImmediatePropagation) { - o.stopImmediatePropagation(); - } - return; - } - }); - - /** This double checks and ensures you can't paste content into the rendered macro */ - editor.on("Paste", function (o) { - if (isOnMacroElement) { - if (o.preventDefault) { - o.preventDefault(); - } - if (o.stopImmediatePropagation) { - o.stopImmediatePropagation(); - } - return; - } - }); - - //set onNodeChanged event listener - editor.on('NodeChange', onNodeChanged); - - /** - * Listen for the keydown in the editor, we'll check if we are currently on a macro element, if so - * we'll check if the key down is a supported key which requires an action, otherwise we ignore the request - * so the macro cannot be edited. - */ - editor.on('KeyDown', function (e) { - if (isOnMacroElement) { - var macroElement = editor.selection.getNode(); - - //get the 'real' element (either p or the real one) - macroElement = getRealMacroElem(macroElement); - - //prevent editing - e.preventDefault(); - e.stopPropagation(); - - var moveSibling = function (element, isNext) { - var $e = $(element); - var $sibling = isNext ? $e.next() : $e.prev(); - if ($sibling.length > 0) { - editor.selection.select($sibling.get(0)); - editor.selection.collapse(true); - } - else { - //if we're moving previous and there is no sibling, then lets recurse and just select the next one - if (!isNext) { - moveSibling(element, true); - return; - } - - //if there is no sibling we'll generate a new p at the end and select it - editor.setContent(editor.getContent() + "

     

    "); - editor.selection.select($(editor.dom.getRoot()).children().last().get(0)); - editor.selection.collapse(true); - - } - }; - - //supported keys to move to the next or prev element (13-enter, 27-esc, 38-up, 40-down, 39-right, 37-left) - //supported keys to remove the macro (8-backspace, 46-delete) - //TODO: Should we make the enter key insert a line break before or leave it as moving to the next element? - if ($.inArray(e.keyCode, [13, 40, 39]) !== -1) { - //move to next element - moveSibling(macroElement, true); - } - else if ($.inArray(e.keyCode, [27, 38, 37]) !== -1) { - //move to prev element - moveSibling(macroElement, false); - } - else if ($.inArray(e.keyCode, [8, 46]) !== -1) { - //delete macro element - - //move first, then delete - moveSibling(macroElement, false); - editor.dom.remove(macroElement); - } - return ; - } - }); - - }, - - /** The insert macro button click event handler */ - onclick: function () { - - var dialogData = { - //flag for use in rte so we only show macros flagged for the editor - richTextEditor: true - }; - - //when we click we could have a macro already selected and in that case we'll want to edit the current parameters - //so we'll need to extract them and submit them to the dialog. - var macroElement = editor.selection.getNode(); - macroElement = getRealMacroElem(macroElement); - if (macroElement) { - //we have a macro selected so we'll need to parse it's alias and parameters - var contents = $(macroElement).contents(); - var comment = _.find(contents, function(item) { - return item.nodeType === 8; - }); - if (!comment) { - throw "Cannot parse the current macro, the syntax in the editor is invalid"; - } - var syntax = comment.textContent.trim(); - var parsed = macroService.parseMacroSyntax(syntax); - dialogData = { - macroData: parsed - }; - } - - dialogService.macroPicker({ - scope: $scope, - dialogData : dialogData, - callback: function(data) { - - //put the macro syntax in comments, we will parse this out on the server side to be used - //for persisting. - var macroSyntaxComment = ""; - //create an id class for this element so we can re-select it after inserting - var uniqueId = "umb-macro-" + editor.dom.uniqueId(); - var macroDiv = editor.dom.create('div', - { - 'class': 'umb-macro-holder ' + data.macroAlias + ' mceNonEditable ' + uniqueId - }, - macroSyntaxComment + 'Macro alias: ' + data.macroAlias + ''); - - editor.selection.setNode(macroDiv); - - var $macroDiv = $(editor.dom.select("div.umb-macro-holder." + uniqueId)); - - //async load the macro content - loadMacroContent($macroDiv, data); - } - }); - - } - }); - } - }; -} - +/** + * @ngdoc service + * @name umbraco.services.tinyMceService + * + * + * @description + * A service containing all logic for all of the Umbraco TinyMCE plugins + */ +function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macroResource, macroService, $routeParams, umbRequestHelper) { + return { + + /** + * @ngdoc method + * @name umbraco.services.tinyMceService#configuration + * @methodOf umbraco.services.tinyMceService + * + * @description + * Returns a collection of plugins available to the tinyMCE editor + * + */ + configuration: function () { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "rteApiBaseUrl", + "GetConfiguration")), + 'Failed to retreive entity data for id '); + }, + + /** + * @ngdoc method + * @name umbraco.services.tinyMceService#defaultPrevalues + * @methodOf umbraco.services.tinyMceService + * + * @description + * Returns a default configration to fallback on in case none is provided + * + */ + defaultPrevalues: function () { + var cfg = {}; + cfg.toolbar = ["code", "bold", "italic", "umbracocss","alignleft", "aligncenter", "alignright", "bullist","numlist", "outdent", "indent", "link", "image", "umbmediapicker", "umbembeddialog", "umbmacro"]; + cfg.stylesheets = []; + cfg.dimensions = {height: 400, width: 600}; + return cfg; + }, + + /** + * @ngdoc method + * @name umbraco.services.tinyMceService#createInsertEmbeddedMedia + * @methodOf umbraco.services.tinyMceService + * + * @description + * Creates the umbrco insert embedded media tinymce plugin + * + * @param {Object} editor the TinyMCE editor instance + * @param {Object} $scope the current controller scope + */ + createInsertEmbeddedMedia: function (editor, $scope) { + editor.addButton('umbembeddialog', { + icon: 'custom icon-tv', + tooltip: 'Embed', + onclick: function () { + dialogService.embedDialog({ + scope: $scope, callback: function (data) { + editor.insertContent(data); + } + }); + } + }); + }, + + /** + * @ngdoc method + * @name umbraco.services.tinyMceService#createMediaPicker + * @methodOf umbraco.services.tinyMceService + * + * @description + * Creates the umbrco insert media tinymce plugin + * + * @param {Object} editor the TinyMCE editor instance + * @param {Object} $scope the current controller scope + */ + createMediaPicker: function (editor, $scope) { + editor.addButton('umbmediapicker', { + icon: 'custom icon-picture', + tooltip: 'Media Picker', + onclick: function () { + dialogService.mediaPicker({ + scope: $scope, callback: function (img) { + + if (img) { + var imagePropVal = imageHelper.getImagePropertyValue({ imageModel: img, scope: $scope }); + var data = { + alt: "Some description", + src: (imagePropVal) ? imagePropVal : "nothing.jpg", + id: '__mcenew' + }; + + + editor.insertContent(editor.dom.createHTML('img', data)); + + $timeout(function () { + var imgElm = editor.dom.get('__mcenew'); + var size = editor.dom.getSize(imgElm); + + var newSize = imageHelper.scaleToMaxSize(500, size.w, size.h); + var s = "width: " + newSize.width + "px; height:" + newSize.height + "px;"; + editor.dom.setAttrib(imgElm, 'style', s); + editor.dom.setAttrib(imgElm, 'rel', newSize.width + "," + newSize.height); + editor.dom.setAttrib(imgElm, 'id', null); + + }, 500); + } + } + }); + } + }); + }, + + /** + * @ngdoc method + * @name umbraco.services.tinyMceService#createLinkPicker + * @methodOf umbraco.services.tinyMceService + * + * @description + * Creates the umbrco insert link tinymce plugin + * + * @param {Object} editor the TinyMCE editor instance + * @param {Object} $scope the current controller scope + */ + createLinkPicker: function (editor, $scope) { + + + + /* + editor.addButton('link', { + icon: 'custom icon-link', + tooltip: 'Link Picker', + onclick: function () { + dialogService.linkPicker({ + scope: $scope, callback: function (link) { + if (link) { + var data = { + title: "Some description", + href: "", + id: '__mcenew' + }; + + editor.execCommand("mceInsertLink", false, { + href: "wat", + title: "muh", + target: null, + "class": null + }); + + + //editor.insertContent(editor.dom.createHTML('a', data)); + } + } + }); + } + });*/ + }, + + /** + * @ngdoc method + * @name umbraco.services.tinyMceService#createUmbracoMacro + * @methodOf umbraco.services.tinyMceService + * + * @description + * Creates the insert umbrco macro tinymce plugin + * + * @param {Object} editor the TinyMCE editor instance + * @param {Object} $scope the current controller scope + */ + createInsertMacro: function (editor, $scope) { + + /** Adds custom rules for the macro plugin and custom serialization */ + editor.on('preInit', function (args) { + //this is requires so that we tell the serializer that a 'div' is actually allowed in the root, otherwise the cleanup will strip it out + editor.serializer.addRules('div'); + + /** This checks if the div is a macro container, if so, checks if its wrapped in a p tag and then unwraps it (removes p tag) */ + editor.serializer.addNodeFilter('div', function (nodes, name) { + for (var i = 0; i < nodes.length; i++) { + if (nodes[i].attr("class") === "umb-macro-holder" && nodes[i].parent && nodes[i].parent.name.toUpperCase() === "P") { + nodes[i].parent.unwrap(); + } + } + }); + + }); + + /** + * Because the macro gets wrapped in a P tag because of the way 'enter' works, this + * method will return the macro element if not wrapped in a p, or the p if the macro + * element is the only one inside of it even if we are deep inside an element inside the macro + */ + function getRealMacroElem(element) { + var e = $(element).closest(".umb-macro-holder"); + if (e.length > 0) { + if (e.get(0).parentNode.nodeName === "P") { + //now check if we're the only element + if (element.parentNode.childNodes.length === 1) { + return e.get(0).parentNode; + } + } + return e.get(0); + } + return null; + } + + /** loads in the macro content async from the server */ + function loadMacroContent($macroDiv, macroData) { + + //if we don't have the macroData, then we'll need to parse it from the macro div + if (!macroData) { + var contents = $macroDiv.contents(); + var comment = _.find(contents, function (item) { + return item.nodeType === 8; + }); + if (!comment) { + throw "Cannot parse the current macro, the syntax in the editor is invalid"; + } + var syntax = comment.textContent.trim(); + var parsed = macroService.parseMacroSyntax(syntax); + macroData = parsed; + } + + var $ins = $macroDiv.find("ins"); + + //show the throbber + $macroDiv.addClass("loading"); + + var contentId = $routeParams.id; + + macroResource.getMacroResultAsHtmlForEditor(macroData.macroAlias, contentId, macroData.marcoParamsDictionary) + .then(function (htmlResult) { + + $macroDiv.removeClass("loading"); + htmlResult = htmlResult.trim(); + if (htmlResult !== "") { + $ins.html(htmlResult); + } + }); + } + + /** Adds the button instance */ + editor.addButton('umbmacro', { + icon: 'custom icon-settings-alt', + tooltip: 'Insert macro', + onPostRender: function () { + + var ctrl = this; + var isOnMacroElement = false; + + /** + if the selection comes from a different element that is not the macro's + we need to check if the selection includes part of the macro, if so we'll force the selection + to clear to the next element since if people can select part of the macro markup they can then modify it. + */ + function handleSelectionChange() { + + if (!editor.selection.isCollapsed()) { + var endSelection = tinymce.activeEditor.selection.getEnd(); + var startSelection = tinymce.activeEditor.selection.getStart(); + //don't proceed if it's an entire element selected + if (endSelection !== startSelection) { + + //if the end selection is a macro then move the cursor + //NOTE: we don't have to handle when the selection comes from a previous parent because + // that is automatically taken care of with the normal onNodeChanged logic since the + // evt.element will be the macro once it becomes part of the selection. + var $testForMacro = $(endSelection).closest(".umb-macro-holder"); + if ($testForMacro.length > 0) { + + //it came from before so move after, if there is no after then select ourselves + var next = $testForMacro.next(); + if (next.length > 0) { + editor.selection.setCursorLocation($testForMacro.next().get(0)); + } + else { + selectMacroElement($testForMacro.get(0)); + } + + } + } + } + } + + /** helper method to select the macro element */ + function selectMacroElement(macroElement) { + // move selection to top element to ensure we can't edit this + editor.selection.select(macroElement); + + // check if the current selection *is* the element (ie bug) + var currentSelection = editor.selection.getStart(); + if (tinymce.isIE) { + if (!editor.dom.hasClass(currentSelection, 'umb-macro-holder')) { + while (!editor.dom.hasClass(currentSelection, 'umb-macro-holder') && currentSelection.parentNode) { + currentSelection = currentSelection.parentNode; + } + editor.selection.select(currentSelection); + } + } + } + + /** + * Add a node change handler, test if we're editing a macro and select the whole thing, then set our isOnMacroElement flag. + * If we change the selection inside this method, then we end up in an infinite loop, so we have to remove ourselves + * from the event listener before changing selection, however, it seems that putting a break point in this method + * will always cause an 'infinite' loop as the caret keeps changing. + */ + function onNodeChanged(evt) { + + //set our macro button active when on a node of class umb-macro-holder + var $macroElement = $(evt.element).closest(".umb-macro-holder"); + + handleSelectionChange(); + + //set the button active + ctrl.active($macroElement.length !== 0); + + if ($macroElement.length > 0) { + var macroElement = $macroElement.get(0); + + //remove the event listener before re-selecting + editor.off('NodeChange', onNodeChanged); + + selectMacroElement(macroElement); + + //set the flag + isOnMacroElement = true; + + //re-add the event listener + editor.on('NodeChange', onNodeChanged); + } + else { + isOnMacroElement = false; + } + + } + + /** when the contents load we need to find any macros declared and load in their content */ + editor.on("LoadContent", function (o) { + + //get all macro divs and load their content + $(editor.dom.select(".umb-macro-holder.mceNonEditable")).each(function() { + loadMacroContent($(this)); + }); + + }); + + /** This prevents any other commands from executing when the current element is the macro so the content cannot be edited */ + editor.on('BeforeExecCommand', function (o) { + if (isOnMacroElement) { + if (o.preventDefault) { + o.preventDefault(); + } + if (o.stopImmediatePropagation) { + o.stopImmediatePropagation(); + } + return; + } + }); + + /** This double checks and ensures you can't paste content into the rendered macro */ + editor.on("Paste", function (o) { + if (isOnMacroElement) { + if (o.preventDefault) { + o.preventDefault(); + } + if (o.stopImmediatePropagation) { + o.stopImmediatePropagation(); + } + return; + } + }); + + //set onNodeChanged event listener + editor.on('NodeChange', onNodeChanged); + + /** + * Listen for the keydown in the editor, we'll check if we are currently on a macro element, if so + * we'll check if the key down is a supported key which requires an action, otherwise we ignore the request + * so the macro cannot be edited. + */ + editor.on('KeyDown', function (e) { + if (isOnMacroElement) { + var macroElement = editor.selection.getNode(); + + //get the 'real' element (either p or the real one) + macroElement = getRealMacroElem(macroElement); + + //prevent editing + e.preventDefault(); + e.stopPropagation(); + + var moveSibling = function (element, isNext) { + var $e = $(element); + var $sibling = isNext ? $e.next() : $e.prev(); + if ($sibling.length > 0) { + editor.selection.select($sibling.get(0)); + editor.selection.collapse(true); + } + else { + //if we're moving previous and there is no sibling, then lets recurse and just select the next one + if (!isNext) { + moveSibling(element, true); + return; + } + + //if there is no sibling we'll generate a new p at the end and select it + editor.setContent(editor.getContent() + "

     

    "); + editor.selection.select($(editor.dom.getRoot()).children().last().get(0)); + editor.selection.collapse(true); + + } + }; + + //supported keys to move to the next or prev element (13-enter, 27-esc, 38-up, 40-down, 39-right, 37-left) + //supported keys to remove the macro (8-backspace, 46-delete) + //TODO: Should we make the enter key insert a line break before or leave it as moving to the next element? + if ($.inArray(e.keyCode, [13, 40, 39]) !== -1) { + //move to next element + moveSibling(macroElement, true); + } + else if ($.inArray(e.keyCode, [27, 38, 37]) !== -1) { + //move to prev element + moveSibling(macroElement, false); + } + else if ($.inArray(e.keyCode, [8, 46]) !== -1) { + //delete macro element + + //move first, then delete + moveSibling(macroElement, false); + editor.dom.remove(macroElement); + } + return ; + } + }); + + }, + + /** The insert macro button click event handler */ + onclick: function () { + + var dialogData = { + //flag for use in rte so we only show macros flagged for the editor + richTextEditor: true + }; + + //when we click we could have a macro already selected and in that case we'll want to edit the current parameters + //so we'll need to extract them and submit them to the dialog. + var macroElement = editor.selection.getNode(); + macroElement = getRealMacroElem(macroElement); + if (macroElement) { + //we have a macro selected so we'll need to parse it's alias and parameters + var contents = $(macroElement).contents(); + var comment = _.find(contents, function(item) { + return item.nodeType === 8; + }); + if (!comment) { + throw "Cannot parse the current macro, the syntax in the editor is invalid"; + } + var syntax = comment.textContent.trim(); + var parsed = macroService.parseMacroSyntax(syntax); + dialogData = { + macroData: parsed + }; + } + + dialogService.macroPicker({ + scope: $scope, + dialogData : dialogData, + callback: function(data) { + + //put the macro syntax in comments, we will parse this out on the server side to be used + //for persisting. + var macroSyntaxComment = ""; + //create an id class for this element so we can re-select it after inserting + var uniqueId = "umb-macro-" + editor.dom.uniqueId(); + var macroDiv = editor.dom.create('div', + { + 'class': 'umb-macro-holder ' + data.macroAlias + ' mceNonEditable ' + uniqueId + }, + macroSyntaxComment + 'Macro alias: ' + data.macroAlias + ''); + + editor.selection.setNode(macroDiv); + + var $macroDiv = $(editor.dom.select("div.umb-macro-holder." + uniqueId)); + + //async load the macro content + loadMacroContent($macroDiv, data); + } + }); + + } + }); + } + }; +} + angular.module('umbraco.services').factory('tinyMceService', tinyMceService); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/dragdrop.less b/src/Umbraco.Web.UI.Client/src/less/dragdrop.less index 74e968051b..05c99f2dd7 100644 --- a/src/Umbraco.Web.UI.Client/src/less/dragdrop.less +++ b/src/Umbraco.Web.UI.Client/src/less/dragdrop.less @@ -1,37 +1,37 @@ -body.dragging, body.dragging * { - cursor: move !important; -} - -li.dragged { - position: absolute; - opacity: 0.5; - z-index: 2000; -} - -.umb-sort li{ - display: block; - margin: 5px; - padding: 5px; - border: 1px solid #CCC; - background: @grayLighter; -} - -.umb-sort .placeholder { - position: relative; - margin: 0; - padding: 0; - border: none; -} - -.umb-sort .placeholder:before { - position: absolute; - content: ""; - width: 0; - height: 0; - margin-top: -5px; - left: -5px; - top: -4px; - border: 5px solid transparent; - border-left-color: red; - border-right: none; +body.dragging, body.dragging * { + cursor: move !important; +} + +li.dragged { + position: absolute; + opacity: 0.5; + z-index: 2000; +} + +.umb-sort li{ + display: block; + margin: 5px; + padding: 5px; + border: 1px solid #CCC; + background: @grayLighter; +} + +.umb-sort .placeholder { + position: relative; + margin: 0; + padding: 0; + border: none; +} + +.umb-sort .placeholder:before { + position: absolute; + content: ""; + width: 0; + height: 0; + margin-top: -5px; + left: -5px; + top: -4px; + border: 5px solid transparent; + border-left-color: red; + border-right: none; } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/forms.less b/src/Umbraco.Web.UI.Client/src/less/forms.less index 5139a42b73..fc4a56b62e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/forms.less +++ b/src/Umbraco.Web.UI.Client/src/less/forms.less @@ -629,11 +629,10 @@ input[type="checkbox"][readonly] { // ----------- input.search-query { - padding-right: 14px; padding-right: 4px \9; padding-left: 14px; padding-left: 4px \9; /* IE7-8 doesn't have border-radius, so don't indent the padding */ - margin-bottom: 0; // Remove the default margin on all inputs + margin: 0; // Remove the default margin on all inputs .border-radius(0px); } diff --git a/src/Umbraco.Web.UI.Client/src/less/grid.less b/src/Umbraco.Web.UI.Client/src/less/grid.less index 5621323022..fc4c00c593 100644 --- a/src/Umbraco.Web.UI.Client/src/less/grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/grid.less @@ -97,7 +97,9 @@ body { right: 0; } -#search-form form{margin-top: 15px;} +#search-form form{ + margin-top: 9px; +} #navigation { left: 80px; diff --git a/src/Umbraco.Web.UI.Client/src/less/navs.less b/src/Umbraco.Web.UI.Client/src/less/navs.less index e92c481615..f4e5b9c610 100644 --- a/src/Umbraco.Web.UI.Client/src/less/navs.less +++ b/src/Umbraco.Web.UI.Client/src/less/navs.less @@ -112,8 +112,8 @@ } .nav-tabs > li > a, .nav-pills > li > a { - padding-right: 12px; - padding-left: 12px; + padding-right: 8px; + padding-left: 8px; margin-right: 2px; line-height: 14px; // keeps the overall height an even number } @@ -133,14 +133,14 @@ .nav-tabs > li > a { -webkit-border-radius: @tabsBorderRadius; border-radius: @tabsBorderRadius; - color: @black; - padding-top: 8px; - padding-bottom: 8px; + color: @gray; + padding-top: 5px; + padding-bottom: 4px; line-height: @baseLineHeight; border: 1px solid transparent; .border-radius(4px 4px 0 0); &:hover { - color: @blue; + color: @black; } &:hover, &:focus { @@ -151,7 +151,7 @@ .nav-tabs > .active > a, .nav-tabs > .active > a:hover, .nav-tabs > .active > a:focus { - color: @gray; + color: @black; background-color: @bodyBackground; border: 1px solid #ddd; border-bottom-color: transparent; diff --git a/src/Umbraco.Web.UI.Client/src/less/panel.less b/src/Umbraco.Web.UI.Client/src/less/panel.less index 060c541449..4db77b7d0f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/panel.less @@ -37,12 +37,28 @@ cursor: text; } -.umb-headline-editor-wrapper, -h1.headline{ +.umb-headline-editor-wrapper h1.headline{ height: 20px; padding: 30px 0 0 20px; position: relative; } +.umb-panel-header .umb-headline { + font-size: 18px; + border: none; + background: none; + margin: 25px 0 0 20px; + padding: 0 0 3px 0; + height: auto; + width: 100%; + border-bottom: 1px dotted #f8f8f8; +} + +.umb-panel-header .umb-headline:hover { + border-bottom: 1px dotted #ccc; + padding: 0 0 3px 0 +} +/* + .umb-headline-editor-wrapper h1, h1.headline { margin: 0; font-size: 14px; @@ -58,6 +74,9 @@ h1.headline{ margin: -10px 0 0 -7px; padding: 10px 0 0 7px } +*/ + + .umb-headline-editor-wrapper .help-inline{ @@ -120,11 +139,9 @@ h1.headline{ .umb-panel-header .umb-btn-toolbar { float: right; - padding: 20px 20px 0 0; + padding: 15px 20px 0 0; } - - .umb-panel-footer { margin: 0; padding: 20px; diff --git a/src/Umbraco.Web.UI.Client/src/less/sections.less b/src/Umbraco.Web.UI.Client/src/less/sections.less index 3f6f10ae74..8b6a6941b5 100644 --- a/src/Umbraco.Web.UI.Client/src/less/sections.less +++ b/src/Umbraco.Web.UI.Client/src/less/sections.less @@ -64,7 +64,7 @@ ul.sections:hover a span { ul.sections li.avatar { height: 69px; - padding: 30px 0 0 0; + padding: 28px 0 2px 0; text-align: center; margin: 0 0 0 -4px; border-bottom: 1px solid @grayDark; diff --git a/src/Umbraco.Web.UI.Client/src/packages/SirTrevor/package.manifest b/src/Umbraco.Web.UI.Client/src/packages/SirTrevor/package.manifest index b84e645395..2e0bcd712a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/SirTrevor/package.manifest +++ b/src/Umbraco.Web.UI.Client/src/packages/SirTrevor/package.manifest @@ -1,16 +1,16 @@ -{ - propertyEditors: [ - { - alias: "Sir.Trevor", - name: "Sir Trevor", - hideLabel: true, - valueType: "JSON", - editor: { - view: "~/App_Plugins/SirTrevor/SirTrevor.html" - } - } - ], - javascript: [ - '~/App_Plugins/SirTrevor/SirTrevor.controller.js' - ] +{ + propertyEditors: [ + { + alias: "Sir.Trevor", + name: "Sir Trevor", + hideLabel: true, + valueType: "JSON", + editor: { + view: "~/App_Plugins/SirTrevor/SirTrevor.html" + } + } + ], + javascript: [ + '~/App_Plugins/SirTrevor/SirTrevor.controller.js' + ] } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.controller.js index 767cc9bc8c..b67c4c0fc5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.controller.js @@ -1,124 +1,124 @@ -/** - * @ngdoc controller - * @name Umbraco.Dialogs.InsertMacroController - * @function - * - * @description - * The controller for the custom insert macro dialog. Until we upgrade the template editor to be angular this - * is actually loaded into an iframe with full html. - */ -function InsertMacroController($scope, entityResource, macroResource, umbPropEditorHelper, macroService, formHelper) { - - /** changes the view to edit the params of the selected macro */ - function editParams() { - //get the macro params if there are any - macroResource.getMacroParameters($scope.selectedMacro.id) - .then(function (data) { - - //go to next page if there are params otherwise we can just exit - if (!angular.isArray(data) || data.length === 0) { - //we can just exist! - submitForm(); - - } else { - $scope.wizardStep = "paramSelect"; - $scope.macroParams = data; - - //fill in the data if we are editing this macro - if ($scope.dialogData && $scope.dialogData.macroData && $scope.dialogData.macroData.marcoParamsDictionary) { - _.each($scope.dialogData.macroData.marcoParamsDictionary, function (val, key) { - var prop = _.find($scope.macroParams, function (item) { - return item.alias == key; - }); - if (prop) { - prop.value = val; - } - }); - - } - } - }); - } - - /** submit the filled out macro params */ - function submitForm() { - - //collect the value data, close the dialog and send the data back to the caller - - //create a dictionary for the macro params - var paramDictionary = {}; - _.each($scope.macroParams, function (item) { - paramDictionary[item.alias] = item.value; - }); - - //need to find the macro alias for the selected id - var macroAlias = $scope.selectedMacro.alias; - - //get the syntax based on the rendering engine - var syntax; - if ($scope.dialogData.renderingEngine && $scope.dialogData.renderingEngine === "WebForms") { - syntax = macroService.generateWebFormsSyntax({ macroAlias: macroAlias, marcoParamsDictionary: paramDictionary }); - } - else if ($scope.dialogData.renderingEngine && $scope.dialogData.renderingEngine === "Mvc") { - syntax = macroService.generateMvcSyntax({ macroAlias: macroAlias, marcoParamsDictionary: paramDictionary }); - } - else { - syntax = macroService.generateMacroSyntax({ macroAlias: macroAlias, marcoParamsDictionary: paramDictionary }); - } - - $scope.submit({ syntax: syntax, macroAlias: macroAlias, marcoParamsDictionary: paramDictionary }); - } - - $scope.macros = []; - $scope.selectedMacro = null; - $scope.wizardStep = "macroSelect"; - $scope.macroParams = []; - - $scope.submitForm = function () { - - if (formHelper.submitForm({ scope: $scope })) { - - formHelper.resetForm({ scope: $scope }); - - if ($scope.wizardStep === "macroSelect") { - editParams(); - } - else { - submitForm(); - } - - } - }; - - //here we check to see if we've been passed a selected macro and if so we'll set the - //editor to start with parameter editing - if ($scope.dialogData && $scope.dialogData.macroData) { - $scope.wizardStep = "paramSelect"; - } - - //get the macro list - pass in a filter if it is only for rte - entityResource.getAll("Macro", ($scope.dialogData && $scope.dialogData.richTextEditor && $scope.dialogData.richTextEditor === true) ? "UseInEditor=true" : null) - .then(function (data) { - - $scope.macros = data; - - //check if there's a pre-selected macro and if it exists - if ($scope.dialogData && $scope.dialogData.macroData && $scope.dialogData.macroData.macroAlias) { - var found = _.find(data, function (item) { - return item.alias === $scope.dialogData.macroData.macroAlias; - }); - if (found) { - //select the macro and go to next screen - $scope.selectedMacro = found; - editParams(); - return; - } - } - //we don't have a pre-selected macro so ensure the correct step is set - $scope.wizardStep = "macroSelect"; - }); - - -} - -angular.module("umbraco").controller("Umbraco.Dialogs.InsertMacroController", InsertMacroController); +/** + * @ngdoc controller + * @name Umbraco.Dialogs.InsertMacroController + * @function + * + * @description + * The controller for the custom insert macro dialog. Until we upgrade the template editor to be angular this + * is actually loaded into an iframe with full html. + */ +function InsertMacroController($scope, entityResource, macroResource, umbPropEditorHelper, macroService, formHelper) { + + /** changes the view to edit the params of the selected macro */ + function editParams() { + //get the macro params if there are any + macroResource.getMacroParameters($scope.selectedMacro.id) + .then(function (data) { + + //go to next page if there are params otherwise we can just exit + if (!angular.isArray(data) || data.length === 0) { + //we can just exist! + submitForm(); + + } else { + $scope.wizardStep = "paramSelect"; + $scope.macroParams = data; + + //fill in the data if we are editing this macro + if ($scope.dialogData && $scope.dialogData.macroData && $scope.dialogData.macroData.marcoParamsDictionary) { + _.each($scope.dialogData.macroData.marcoParamsDictionary, function (val, key) { + var prop = _.find($scope.macroParams, function (item) { + return item.alias == key; + }); + if (prop) { + prop.value = val; + } + }); + + } + } + }); + } + + /** submit the filled out macro params */ + function submitForm() { + + //collect the value data, close the dialog and send the data back to the caller + + //create a dictionary for the macro params + var paramDictionary = {}; + _.each($scope.macroParams, function (item) { + paramDictionary[item.alias] = item.value; + }); + + //need to find the macro alias for the selected id + var macroAlias = $scope.selectedMacro.alias; + + //get the syntax based on the rendering engine + var syntax; + if ($scope.dialogData.renderingEngine && $scope.dialogData.renderingEngine === "WebForms") { + syntax = macroService.generateWebFormsSyntax({ macroAlias: macroAlias, marcoParamsDictionary: paramDictionary }); + } + else if ($scope.dialogData.renderingEngine && $scope.dialogData.renderingEngine === "Mvc") { + syntax = macroService.generateMvcSyntax({ macroAlias: macroAlias, marcoParamsDictionary: paramDictionary }); + } + else { + syntax = macroService.generateMacroSyntax({ macroAlias: macroAlias, marcoParamsDictionary: paramDictionary }); + } + + $scope.submit({ syntax: syntax, macroAlias: macroAlias, marcoParamsDictionary: paramDictionary }); + } + + $scope.macros = []; + $scope.selectedMacro = null; + $scope.wizardStep = "macroSelect"; + $scope.macroParams = []; + + $scope.submitForm = function () { + + if (formHelper.submitForm({ scope: $scope })) { + + formHelper.resetForm({ scope: $scope }); + + if ($scope.wizardStep === "macroSelect") { + editParams(); + } + else { + submitForm(); + } + + } + }; + + //here we check to see if we've been passed a selected macro and if so we'll set the + //editor to start with parameter editing + if ($scope.dialogData && $scope.dialogData.macroData) { + $scope.wizardStep = "paramSelect"; + } + + //get the macro list - pass in a filter if it is only for rte + entityResource.getAll("Macro", ($scope.dialogData && $scope.dialogData.richTextEditor && $scope.dialogData.richTextEditor === true) ? "UseInEditor=true" : null) + .then(function (data) { + + $scope.macros = data; + + //check if there's a pre-selected macro and if it exists + if ($scope.dialogData && $scope.dialogData.macroData && $scope.dialogData.macroData.macroAlias) { + var found = _.find(data, function (item) { + return item.alias === $scope.dialogData.macroData.macroAlias; + }); + if (found) { + //select the macro and go to next screen + $scope.selectedMacro = found; + editParams(); + return; + } + } + //we don't have a pre-selected macro so ensure the correct step is set + $scope.wizardStep = "macroSelect"; + }); + + +} + +angular.module("umbraco").controller("Umbraco.Dialogs.InsertMacroController", InsertMacroController); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.html index 028653912b..16c6d6fcc3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.html @@ -1,46 +1,46 @@ -
    -
    - - - -
    - - - - - - - -
    - -
    {{$parent.$parent.selectedMacro.name}}
    - -
      -
    • - - - - - - - -
    • -
    - -
    - -
    - -
    -
    +
    +
    + + + +
    + + + + + + + +
    + +
    {{$parent.$parent.selectedMacro.name}}
    + +
      +
    • + + + + + + + +
    • +
    + +
    + +
    + +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/linkpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/linkpicker.controller.js index 1e584740a0..52195bc253 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/linkpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/linkpicker.controller.js @@ -1,68 +1,68 @@ -//used for the media picker dialog -angular.module("umbraco").controller("Umbraco.Dialogs.LinkPickerController", - function ($scope, eventsService, entityResource, contentResource, $log) { - var dialogOptions = $scope.$parent.dialogOptions; - - $scope.dialogTreeEventHandler = $({}); - $scope.target = {}; - - if(dialogOptions.currentTarget){ - $scope.target = dialogOptions.currentTarget; - - //if we a node ID, we fetch the current node to build the form data - if($scope.target.id){ - - if(!$scope.target.path) { - entityResource.getPath($scope.target.id, "Document").then(function (path) { - $scope.target.path = path; - }); - } - - contentResource.getNiceUrl($scope.target.id).then(function(url){ - $scope.target.url = angular.fromJson(url); - }); - } - } - - - $scope.dialogTreeEventHandler.bind("treeNodeSelect", function(ev, args){ - args.event.preventDefault(); - args.event.stopPropagation(); - - eventsService.publish("Umbraco.Dialogs.LinkPickerController.Select", args).then(function(args){ - var c = $(args.event.target.parentElement); - - //renewing - if(args.node !== $scope.target){ - if($scope.selectedEl){ - $scope.selectedEl.find(".temporary").remove(); - $scope.selectedEl.find("i.umb-tree-icon").show(); - } - - $scope.selectedEl = c; - $scope.target = args.node; - $scope.target.name = args.node.name; - - $scope.selectedEl.find("i.umb-tree-icon") - .hide() - .after(""); - - if(args.node.id < 0){ - $scope.target.url = "/"; - }else{ - contentResource.getNiceUrl(args.node.id).then(function(url){ - $scope.target.url = angular.fromJson(url); - }); - } - }else{ - $scope.target = undefined; - //resetting - if($scope.selectedEl){ - $scope.selectedEl.find(".temporary").remove(); - $scope.selectedEl.find("i.umb-tree-icon").show(); - } - } - }); - - }); +//used for the media picker dialog +angular.module("umbraco").controller("Umbraco.Dialogs.LinkPickerController", + function ($scope, eventsService, entityResource, contentResource, $log) { + var dialogOptions = $scope.$parent.dialogOptions; + + $scope.dialogTreeEventHandler = $({}); + $scope.target = {}; + + if(dialogOptions.currentTarget){ + $scope.target = dialogOptions.currentTarget; + + //if we a node ID, we fetch the current node to build the form data + if($scope.target.id){ + + if(!$scope.target.path) { + entityResource.getPath($scope.target.id, "Document").then(function (path) { + $scope.target.path = path; + }); + } + + contentResource.getNiceUrl($scope.target.id).then(function(url){ + $scope.target.url = angular.fromJson(url); + }); + } + } + + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", function(ev, args){ + args.event.preventDefault(); + args.event.stopPropagation(); + + eventsService.publish("Umbraco.Dialogs.LinkPickerController.Select", args).then(function(args){ + var c = $(args.event.target.parentElement); + + //renewing + if(args.node !== $scope.target){ + if($scope.selectedEl){ + $scope.selectedEl.find(".temporary").remove(); + $scope.selectedEl.find("i.umb-tree-icon").show(); + } + + $scope.selectedEl = c; + $scope.target = args.node; + $scope.target.name = args.node.name; + + $scope.selectedEl.find("i.umb-tree-icon") + .hide() + .after(""); + + if(args.node.id < 0){ + $scope.target.url = "/"; + }else{ + contentResource.getNiceUrl(args.node.id).then(function(url){ + $scope.target.url = angular.fromJson(url); + }); + } + }else{ + $scope.target = undefined; + //resetting + if($scope.selectedEl){ + $scope.selectedEl.find(".temporary").remove(); + $scope.selectedEl.find("i.umb-tree-icon").show(); + } + } + }); + + }); }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/linkpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/linkpicker.html index 7aeb23eec5..1c6922bbd6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/linkpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/linkpicker.html @@ -1,47 +1,47 @@ -
    - - - +
    + + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/membergrouppicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/membergrouppicker.controller.js index d9ea9f3a52..df9f7bc729 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/membergrouppicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/membergrouppicker.controller.js @@ -1,68 +1,68 @@ -//used for the member picker dialog -angular.module("umbraco").controller("Umbraco.Dialogs.MemberGroupPickerController", - function($scope, eventsService, entityResource, searchService, $log) { - var dialogOptions = $scope.$parent.dialogOptions; - $scope.dialogTreeEventHandler = $({}); - $scope.results = []; - $scope.dialogData = []; - - /** Method used for selecting a node */ - function select(text, id) { - - - $scope.showSearch = false; - $scope.results = []; - $scope.term = ""; - $scope.oldTerm = undefined; - - if (dialogOptions.multiPicker) { - if ($scope.dialogData.indexOf(id) == -1) { - $scope.dialogData.push(id); - } - } - else { - $scope.submit(id); - - } - } - - function remove(text, id) { - var index = $scope.dialogData.indexOf(id); - - if (index > -1) { - $scope.dialogData.splice(index, 1); - } - } - - - $scope.dialogTreeEventHandler.bind("treeNodeSelect", function(ev, args) { - args.event.preventDefault(); - args.event.stopPropagation(); - - - eventsService.publish("Umbraco.Dialogs.MemberGroupPickerController.Select", args).then(function(a) { - - //This is a tree node, so we don't have an entity to pass in, it will need to be looked up - //from the server in this method. - select(a.node.name, a.node.id); - - if (dialogOptions.multiPicker) { - var c = $(a.event.target.parentElement); - if (!a.node.selected) { - a.node.selected = true; - c.find("i.umb-tree-icon").hide() - .after(""); - } - else { - - remove(a.node.name, a.node.id); - - a.node.selected = false; - c.find(".temporary").remove(); - c.find("i.umb-tree-icon").show(); - } - } - }); - - }); +//used for the member picker dialog +angular.module("umbraco").controller("Umbraco.Dialogs.MemberGroupPickerController", + function($scope, eventsService, entityResource, searchService, $log) { + var dialogOptions = $scope.$parent.dialogOptions; + $scope.dialogTreeEventHandler = $({}); + $scope.results = []; + $scope.dialogData = []; + + /** Method used for selecting a node */ + function select(text, id) { + + + $scope.showSearch = false; + $scope.results = []; + $scope.term = ""; + $scope.oldTerm = undefined; + + if (dialogOptions.multiPicker) { + if ($scope.dialogData.indexOf(id) == -1) { + $scope.dialogData.push(id); + } + } + else { + $scope.submit(id); + + } + } + + function remove(text, id) { + var index = $scope.dialogData.indexOf(id); + + if (index > -1) { + $scope.dialogData.splice(index, 1); + } + } + + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", function(ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); + + + eventsService.publish("Umbraco.Dialogs.MemberGroupPickerController.Select", args).then(function(a) { + + //This is a tree node, so we don't have an entity to pass in, it will need to be looked up + //from the server in this method. + select(a.node.name, a.node.id); + + if (dialogOptions.multiPicker) { + var c = $(a.event.target.parentElement); + if (!a.node.selected) { + a.node.selected = true; + c.find("i.umb-tree-icon").hide() + .after(""); + } + else { + + remove(a.node.name, a.node.id); + + a.node.selected = false; + c.find(".temporary").remove(); + c.find("i.umb-tree-icon").show(); + } + } + }); + + }); }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/membergrouppicker.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/membergrouppicker.html index 39ee807763..fdd2aa7378 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/membergrouppicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/membergrouppicker.html @@ -1,36 +1,36 @@ -
    - - - - +
    + + + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/memberpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/memberpicker.controller.js index c96c82b9c5..39b03dca57 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/memberpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/memberpicker.controller.js @@ -1,88 +1,88 @@ -//used for the member picker dialog -angular.module("umbraco").controller("Umbraco.Dialogs.MemberPickerController", - function($scope, eventsService, entityResource, searchService, $log) { - var dialogOptions = $scope.$parent.dialogOptions; - $scope.dialogTreeEventHandler = $({}); - $scope.results = []; - - /** Method used for selecting a node */ - function select(text, key, entity) { - - $scope.showSearch = false; - $scope.results = []; - $scope.term = ""; - $scope.oldTerm = undefined; - - if (dialogOptions.multiPicker) { - $scope.select(key); - } - else { - //if an entity has been passed in, use it - if (entity) { - $scope.submit(entity); - } - else { - //otherwise we have to get it from the server - entityResource.getById(key, "Member").then(function (ent) { - $scope.submit(ent); - }); - } - } - } - - $scope.performSearch = function() { - if ($scope.term) { - if ($scope.oldTerm !== $scope.term) { - $scope.results = []; - searchService.searchMembers({ term: $scope.term }).then(function(data) { - $scope.results = data; - }); - $scope.showSearch = true; - $scope.oldTerm = $scope.term; - } - } - else { - $scope.oldTerm = ""; - $scope.showSearch = false; - $scope.results = []; - } - }; - - /** method to select a search result */ - $scope.selectResult = function(result) { - //since result = an entity, we'll pass it in so we don't have to go back to the server - select(result.name, result.id, result); - }; - - $scope.dialogTreeEventHandler.bind("treeNodeSelect", function(ev, args) { - args.event.preventDefault(); - args.event.stopPropagation(); - - if (args.node.nodeType === "member-folder") { - return; - } - - eventsService.publish("Umbraco.Dialogs.MemberPickerController.Select", args).then(function(a) { - - //This is a tree node, so we don't have an entity to pass in, it will need to be looked up - //from the server in this method. - select(a.node.name, a.node.id); - - if (dialogOptions && dialogOptions.multipicker) { - - var c = $(a.event.target.parentElement); - if (!a.node.selected) { - a.node.selected = true; - c.find("i.umb-tree-icon").hide() - .after(""); - } - else { - a.node.selected = false; - c.find(".temporary").remove(); - c.find("i.umb-tree-icon").show(); - } - } - }); - - }); +//used for the member picker dialog +angular.module("umbraco").controller("Umbraco.Dialogs.MemberPickerController", + function($scope, eventsService, entityResource, searchService, $log) { + var dialogOptions = $scope.$parent.dialogOptions; + $scope.dialogTreeEventHandler = $({}); + $scope.results = []; + + /** Method used for selecting a node */ + function select(text, key, entity) { + + $scope.showSearch = false; + $scope.results = []; + $scope.term = ""; + $scope.oldTerm = undefined; + + if (dialogOptions.multiPicker) { + $scope.select(key); + } + else { + //if an entity has been passed in, use it + if (entity) { + $scope.submit(entity); + } + else { + //otherwise we have to get it from the server + entityResource.getById(key, "Member").then(function (ent) { + $scope.submit(ent); + }); + } + } + } + + $scope.performSearch = function() { + if ($scope.term) { + if ($scope.oldTerm !== $scope.term) { + $scope.results = []; + searchService.searchMembers({ term: $scope.term }).then(function(data) { + $scope.results = data; + }); + $scope.showSearch = true; + $scope.oldTerm = $scope.term; + } + } + else { + $scope.oldTerm = ""; + $scope.showSearch = false; + $scope.results = []; + } + }; + + /** method to select a search result */ + $scope.selectResult = function(result) { + //since result = an entity, we'll pass it in so we don't have to go back to the server + select(result.name, result.id, result); + }; + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", function(ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); + + if (args.node.nodeType === "member-folder") { + return; + } + + eventsService.publish("Umbraco.Dialogs.MemberPickerController.Select", args).then(function(a) { + + //This is a tree node, so we don't have an entity to pass in, it will need to be looked up + //from the server in this method. + select(a.node.name, a.node.id); + + if (dialogOptions && dialogOptions.multipicker) { + + var c = $(a.event.target.parentElement); + if (!a.node.selected) { + a.node.selected = true; + c.find("i.umb-tree-icon").hide() + .after(""); + } + else { + a.node.selected = false; + c.find(".temporary").remove(); + c.find("i.umb-tree-icon").show(); + } + } + }); + + }); }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/memberpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/memberpicker.html index 68ce141ce4..360fbb3d63 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/memberpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/memberpicker.html @@ -1,68 +1,68 @@ -
    -
    -
    - -
    -
    - - - +
    +
    +
    + +
    +
    + + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/rteembed.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/rteembed.controller.js index 07fb7ab332..d2ef92317a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/rteembed.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/rteembed.controller.js @@ -1,76 +1,76 @@ -angular.module("umbraco").controller("Umbraco.Dialogs.RteEmbedController", function ($scope, $http) { - $scope.form = {}; - $scope.form.url = ""; - $scope.form.width = 360; - $scope.form.height = 240; - $scope.form.constrain = true; - $scope.form.preview = ""; - $scope.form.success = false; - $scope.form.info = ""; - $scope.form.supportsDimensions = false; - - var origWidth = 500; - var origHeight = 300; - - $scope.showPreview = function(){ - - if ($scope.form.url != "") { - $scope.form.show = true; - $scope.form.preview = "
    "; - $scope.form.info = ""; - $scope.form.success = false; - - $http({ method: 'GET', url: '/umbraco/UmbracoApi/RteEmbed/GetEmbed', params: { url: $scope.form.url, width: $scope.form.width, height: $scope.form.height } }) - .success(function (data) { - - $scope.form.preview = ""; - - switch (data.Status) { - case 0: - //not supported - $scope.form.info = "Not Supported"; - break; - case 1: - //error - $scope.form.info = "Computer says no"; - break; - case 2: - $scope.form.preview = data.Markup; - $scope.form.supportsDimensions = data.SupportsDimensions; - $scope.form.success = true; - break; - } - }) - .error(function() { - $scope.form.preview = ""; - $scope.form.info = "Computer says no"; - }); - - } - - }; - - $scope.changeSize = function (type) { - var width, height; - - if ($scope.form.constrain) { - width = parseInt($scope.form.width, 10); - height = parseInt($scope.form.height, 10); - if (type == 'width') { - origHeight = Math.round((width / origWidth) * height); - $scope.form.height = origHeight; - } else { - origWidth = Math.round((height / origHeight) * width); - $scope.form.width = origWidth; - } - } - if ($scope.form.url != "") { - $scope.showPreview(); - } - - }; - - $scope.insert = function(){ - $scope.submit($scope.form.preview); - }; +angular.module("umbraco").controller("Umbraco.Dialogs.RteEmbedController", function ($scope, $http) { + $scope.form = {}; + $scope.form.url = ""; + $scope.form.width = 360; + $scope.form.height = 240; + $scope.form.constrain = true; + $scope.form.preview = ""; + $scope.form.success = false; + $scope.form.info = ""; + $scope.form.supportsDimensions = false; + + var origWidth = 500; + var origHeight = 300; + + $scope.showPreview = function(){ + + if ($scope.form.url != "") { + $scope.form.show = true; + $scope.form.preview = "
    "; + $scope.form.info = ""; + $scope.form.success = false; + + $http({ method: 'GET', url: '/umbraco/UmbracoApi/RteEmbed/GetEmbed', params: { url: $scope.form.url, width: $scope.form.width, height: $scope.form.height } }) + .success(function (data) { + + $scope.form.preview = ""; + + switch (data.Status) { + case 0: + //not supported + $scope.form.info = "Not Supported"; + break; + case 1: + //error + $scope.form.info = "Computer says no"; + break; + case 2: + $scope.form.preview = data.Markup; + $scope.form.supportsDimensions = data.SupportsDimensions; + $scope.form.success = true; + break; + } + }) + .error(function() { + $scope.form.preview = ""; + $scope.form.info = "Computer says no"; + }); + + } + + }; + + $scope.changeSize = function (type) { + var width, height; + + if ($scope.form.constrain) { + width = parseInt($scope.form.width, 10); + height = parseInt($scope.form.height, 10); + if (type == 'width') { + origHeight = Math.round((width / origWidth) * height); + $scope.form.height = origHeight; + } else { + origWidth = Math.round((height / origHeight) * width); + $scope.form.width = origWidth; + } + } + if ($scope.form.url != "") { + $scope.showPreview(); + } + + }; + + $scope.insert = function(){ + $scope.submit($scope.form.preview); + }; }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/rteembed.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/rteembed.html index df544a74ae..57efd660e0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/rteembed.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/rteembed.html @@ -1,35 +1,35 @@ -
    -
    - - - -
    -
    +
    +
    + + + +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/treepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/treepicker.controller.js index 7568a2705d..9358ea08b8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/treepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/treepicker.controller.js @@ -1,201 +1,201 @@ -//used for the media picker dialog -angular.module("umbraco").controller("Umbraco.Dialogs.TreePickerController", - function ($scope, entityResource, eventsService, $log, searchService) { - - var dialogOptions = $scope.$parent.dialogOptions; - $scope.dialogTreeEventHandler = $({}); - $scope.section = dialogOptions.section; - $scope.treeAlias = dialogOptions.treeAlias; - $scope.multiPicker = dialogOptions.multiPicker; - - //search defaults - $scope.searcher = searchService.searchContent; - $scope.entityType = "Document"; - $scope.results = []; - - //min / max values - if(dialogOptions.minNumber){ - dialogOptions.minNumber = parseInt(dialogOptions.minNumber, 10); - } - if(dialogOptions.maxNumber){ - dialogOptions.maxNumber = parseInt(dialogOptions.maxNumber, 10); - } - - //search - if (dialogOptions.section === "member") { - $scope.searcher = searchService.searchMembers; - $scope.entityType = "Member"; - } - else if (dialogOptions.section === "media") { - $scope.searcher = searchService.searchMedia; - $scope.entityType = "Media"; - } - - //Configures filtering - if (dialogOptions.filter) { - - dialogOptions.filterExclude = true; - dialogOptions.filterAdvanced = false; - - if(dialogOptions.filter[0] === "!"){ - dialogOptions.filterExclude = false; - dialogOptions.filter = dialogOptions.filter.substring(1); - } - - //used advanced filtering - if(dialogOptions.filter[0] === "{"){ - dialogOptions.filterAdvanced = true; - } - - $scope.dialogTreeEventHandler.bind("treeNodeExpanded", function (ev, args) { - if (angular.isArray(args.children)) { - performFiltering(args.children); - } - }); - } - - - /** Method used for selecting a node */ - function select(text, id, entity) { - //if we get the root, we just return a constructed entity, no need for server data - if (id < 0) { - if ($scope.multiPicker) { - $scope.select(id); - } - else { - var node = { - alias: null, - icon: "icon-folder", - id: id, - name: text - }; - $scope.submit(node); - } - }else { - $scope.showSearch = false; - $scope.results = []; - $scope.term = ""; - $scope.oldTerm = undefined; - - if ($scope.multiPicker) { - $scope.select(id); - }else { - //if an entity has been passed in, use it - if (entity) { - $scope.submit(entity); - }else { - //otherwise we have to get it from the server - entityResource.getById(id, $scope.entityType).then(function (ent) { - $scope.submit(ent); - }); - } - } - } - } - - function performFiltering(nodes){ - if(dialogOptions.filterAdvanced){ - angular.forEach(_.where(nodes, angular.fromJson(dialogOptions.filter)), function(value, key){ - value.filtered = true; - if(dialogOptions.filterCssClass){ - value.cssClasses.push(dialogOptions.filterCssClass); - } - }); - }else{ - var a = dialogOptions.filter.split(','); - angular.forEach(nodes, function (value, key) { - - var found = a.indexOf(value.metaData.contentType) >= 0; - if ((dialogOptions.filterExclude && found) || !found) { - value.filtered = true; - if(dialogOptions.filterCssClass){ - value.cssClasses.push(dialogOptions.filterCssClass); - } - } - }); - } - } - - - - $scope.multiSubmit = function (result) { - - if(dialogOptions.minNumber || dialogOptions.maxNumber){ - if(dialogOptions.minNumber > result.length || dialogOptions.maxNumber < result.length){ - return; - } - } - - entityResource.getByIds(result, $scope.entityType).then(function (ents) { - $scope.submit(ents); - }); - }; - - - /** method to select a search result */ - $scope.selectResult = function (result) { - //since result = an entity, we'll pass it in so we don't have to go back to the server - select(result.name, result.id, result); - }; - - - $scope.performSearch = function () { - if ($scope.term) { - if ($scope.oldTerm !== $scope.term) { - $scope.results = []; - - $scope.searcher({ term: $scope.term }).then(function (data) { - $scope.results = data; - }); - - $scope.showSearch = true; - $scope.oldTerm = $scope.term; - } - } - else { - $scope.oldTerm = ""; - $scope.showSearch = false; - $scope.results = []; - } - }; - - - - - //wires up selection - $scope.dialogTreeEventHandler.bind("treeNodeSelect", function (ev, args) { - args.event.preventDefault(); - args.event.stopPropagation(); - - eventsService.publish("Umbraco.Dialogs.TreePickerController.Select", args).then(function (a) { - if (a.node.filtered) { - return; - } - - //This is a tree node, so we don't have an entity to pass in, it will need to be looked up - //from the server in this method. - select(a.node.name, a.node.id); - - //ui... - if ($scope.multiPicker) { - var c = $(a.event.target.parentElement); - if (!a.node.selected) { - a.node.selected = true; - var temp = ""; - var icon = c.find("i.umb-tree-icon"); - if (icon.length > 0) { - icon.hide().after(temp); - } - else { - c.prepend(temp); - } - } - else { - a.node.selected = false; - c.find(".temporary").remove(); - c.find("i.umb-tree-icon").show(); - } - } - }); - }); +//used for the media picker dialog +angular.module("umbraco").controller("Umbraco.Dialogs.TreePickerController", + function ($scope, entityResource, eventsService, $log, searchService) { + + var dialogOptions = $scope.$parent.dialogOptions; + $scope.dialogTreeEventHandler = $({}); + $scope.section = dialogOptions.section; + $scope.treeAlias = dialogOptions.treeAlias; + $scope.multiPicker = dialogOptions.multiPicker; + + //search defaults + $scope.searcher = searchService.searchContent; + $scope.entityType = "Document"; + $scope.results = []; + + //min / max values + if(dialogOptions.minNumber){ + dialogOptions.minNumber = parseInt(dialogOptions.minNumber, 10); + } + if(dialogOptions.maxNumber){ + dialogOptions.maxNumber = parseInt(dialogOptions.maxNumber, 10); + } + + //search + if (dialogOptions.section === "member") { + $scope.searcher = searchService.searchMembers; + $scope.entityType = "Member"; + } + else if (dialogOptions.section === "media") { + $scope.searcher = searchService.searchMedia; + $scope.entityType = "Media"; + } + + //Configures filtering + if (dialogOptions.filter) { + + dialogOptions.filterExclude = true; + dialogOptions.filterAdvanced = false; + + if(dialogOptions.filter[0] === "!"){ + dialogOptions.filterExclude = false; + dialogOptions.filter = dialogOptions.filter.substring(1); + } + + //used advanced filtering + if(dialogOptions.filter[0] === "{"){ + dialogOptions.filterAdvanced = true; + } + + $scope.dialogTreeEventHandler.bind("treeNodeExpanded", function (ev, args) { + if (angular.isArray(args.children)) { + performFiltering(args.children); + } + }); + } + + + /** Method used for selecting a node */ + function select(text, id, entity) { + //if we get the root, we just return a constructed entity, no need for server data + if (id < 0) { + if ($scope.multiPicker) { + $scope.select(id); + } + else { + var node = { + alias: null, + icon: "icon-folder", + id: id, + name: text + }; + $scope.submit(node); + } + }else { + $scope.showSearch = false; + $scope.results = []; + $scope.term = ""; + $scope.oldTerm = undefined; + + if ($scope.multiPicker) { + $scope.select(id); + }else { + //if an entity has been passed in, use it + if (entity) { + $scope.submit(entity); + }else { + //otherwise we have to get it from the server + entityResource.getById(id, $scope.entityType).then(function (ent) { + $scope.submit(ent); + }); + } + } + } + } + + function performFiltering(nodes){ + if(dialogOptions.filterAdvanced){ + angular.forEach(_.where(nodes, angular.fromJson(dialogOptions.filter)), function(value, key){ + value.filtered = true; + if(dialogOptions.filterCssClass){ + value.cssClasses.push(dialogOptions.filterCssClass); + } + }); + }else{ + var a = dialogOptions.filter.split(','); + angular.forEach(nodes, function (value, key) { + + var found = a.indexOf(value.metaData.contentType) >= 0; + if ((dialogOptions.filterExclude && found) || !found) { + value.filtered = true; + if(dialogOptions.filterCssClass){ + value.cssClasses.push(dialogOptions.filterCssClass); + } + } + }); + } + } + + + + $scope.multiSubmit = function (result) { + + if(dialogOptions.minNumber || dialogOptions.maxNumber){ + if(dialogOptions.minNumber > result.length || dialogOptions.maxNumber < result.length){ + return; + } + } + + entityResource.getByIds(result, $scope.entityType).then(function (ents) { + $scope.submit(ents); + }); + }; + + + /** method to select a search result */ + $scope.selectResult = function (result) { + //since result = an entity, we'll pass it in so we don't have to go back to the server + select(result.name, result.id, result); + }; + + + $scope.performSearch = function () { + if ($scope.term) { + if ($scope.oldTerm !== $scope.term) { + $scope.results = []; + + $scope.searcher({ term: $scope.term }).then(function (data) { + $scope.results = data; + }); + + $scope.showSearch = true; + $scope.oldTerm = $scope.term; + } + } + else { + $scope.oldTerm = ""; + $scope.showSearch = false; + $scope.results = []; + } + }; + + + + + //wires up selection + $scope.dialogTreeEventHandler.bind("treeNodeSelect", function (ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); + + eventsService.publish("Umbraco.Dialogs.TreePickerController.Select", args).then(function (a) { + if (a.node.filtered) { + return; + } + + //This is a tree node, so we don't have an entity to pass in, it will need to be looked up + //from the server in this method. + select(a.node.name, a.node.id); + + //ui... + if ($scope.multiPicker) { + var c = $(a.event.target.parentElement); + if (!a.node.selected) { + a.node.selected = true; + var temp = ""; + var icon = c.find("i.umb-tree-icon"); + if (icon.length > 0) { + icon.hide().after(temp); + } + else { + c.prepend(temp); + } + } + else { + a.node.selected = false; + c.find(".temporary").remove(); + c.find("i.umb-tree-icon").show(); + } + } + }); + }); }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/treepicker.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/treepicker.html index 55ce2e2238..0f0a91c382 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/treepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/treepicker.html @@ -1,69 +1,69 @@ -
    -
    -
    - -
    -
    - - - - -
    +
    +
    +
    + +
    +
    + + + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js index d28b8aaa5a..3e2521b08f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js @@ -1,13 +1,13 @@ -/** This controller is simply here to launch the login dialog when the route is explicitly changed to /login */ -angular.module('umbraco').controller("Umbraco.LoginController", function ($scope, userService, $location) { - - userService._showLoginDialog(); - - //when a user is authorized redirect - this will only be handled here when we are actually on the /login route - $scope.$on("authenticated", function(evt, data) { - //reset the avatar - $scope.avatar = "assets/img/application/logo.png"; - $location.path("/").search(""); - }); - +/** This controller is simply here to launch the login dialog when the route is explicitly changed to /login */ +angular.module('umbraco').controller("Umbraco.LoginController", function ($scope, userService, $location) { + + userService._showLoginDialog(); + + //when a user is authorized redirect - this will only be handled here when we are actually on the /login route + $scope.$on("authenticated", function(evt, data) { + //reset the avatar + $scope.avatar = "assets/img/application/logo.png"; + $location.path("/").search(""); + }); + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/login.html b/src/Umbraco.Web.UI.Client/src/views/common/login.html index d490d395eb..a81cd1e9ae 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/login.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/login.html @@ -1,3 +1,3 @@ -
    - +
    +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js index 86c59c2463..f3fdd6aab7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js @@ -1,241 +1,241 @@ -/** - * @ngdoc controller - * @name Umbraco.Editors.Content.EditController - * @function - * - * @description - * The controller for the content editor - */ -function ContentEditController($scope, $routeParams, $q, $timeout, $window, contentResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, treeService, fileManager, formHelper, umbRequestHelper) { - - $scope.defaultButton = null; - $scope.subButtons = []; - $scope.nav = navigationService; - - //This sets up the action buttons based on what permissions the user has. - //The allowedActions parameter contains a list of chars, each represents a button by permission so - //here we'll build the buttons according to the chars of the user. - function configureButtons(content) { - //reset - $scope.subButtons = []; - - //This is the ideal button order but depends on circumstance, we'll use this array to create the button list - // Publish, SendToPublish, Save - var buttonOrder = ["U", "H", "A"]; - - //Create the first button (primary button) - //We cannot have the Save or SaveAndPublish buttons if they don't have create permissions when we are creating a new item. - if (!$routeParams.create || _.contains(content.allowedActions, "C")) { - for (var b in buttonOrder) { - if (_.contains(content.allowedActions, buttonOrder[b])) { - $scope.defaultButton = createButtonDefinition(buttonOrder[b]); - break; - } - } - } - - //Now we need to make the drop down button list, this is also slightly tricky because: - //We cannot have any buttons if there's no default button above. - //We cannot have the unpublish button (Z) when there's no publish permission. - //We cannot have the unpublish button (Z) when the item is not published. - if ($scope.defaultButton) { - - //get the last index of the button order - var lastIndex = _.indexOf(buttonOrder, $scope.defaultButton.letter); - //add the remaining - for (var i = lastIndex + 1; i < buttonOrder.length; i++) { - if (_.contains(content.allowedActions, buttonOrder[i])) { - $scope.subButtons.push(createButtonDefinition(buttonOrder[i])); - } - } - - //if we are not creating, then we should add unpublish too, - // so long as it's already published and if the user has access to publish - if (!$routeParams.create) { - if (content.publishDate && _.contains(content.allowedActions,"U")) { - $scope.subButtons.push(createButtonDefinition("Z")); - } - } - } - } - - function createButtonDefinition(ch) { - switch (ch) { - case "U": - //publish action - return { - letter: ch, - labelKey: "buttons_saveAndPublish", - handler: $scope.saveAndPublish, - hotKey: "ctrl+p" - }; - case "H": - //send to publish - return { - letter: ch, - labelKey: "buttons_saveToPublish", - handler: $scope.sendToPublish, - hotKey: "ctrl+t" - }; - case "A": - //save - return { - letter: ch, - labelKey: "buttons_save", - handler: $scope.save, - hotKey: "ctrl+s" - }; - case "Z": - //unpublish - return { - letter: ch, - labelKey: "content_unPublish", - handler: $scope.unPublish - }; - default: - return null; - } - } - - /** This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish */ - function performSave(args) { - var deferred = $q.defer(); - - if (formHelper.submitForm({ scope: $scope, statusMessage: args.statusMessage })) { - - args.saveMethod($scope.content, $routeParams.create, fileManager.getFiles()) - .then(function (data) { - - formHelper.resetForm({ scope: $scope, notifications: data.notifications }); - - contentEditingHelper.handleSuccessfulSave({ - scope: $scope, - newContent: data, - rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data) - }); - - configureButtons(data); - - navigationService.syncPath(data.path.split(","), true); - - deferred.resolve(data); - - }, function (err) { - - contentEditingHelper.handleSaveError({ - redirectOnFailure: true, - err: err, - allNewProps: contentEditingHelper.getAllProps(err.data), - allOrigProps: contentEditingHelper.getAllProps($scope.content) - }); - - deferred.reject(err); - }); - } - else { - deferred.reject(); - } - - return deferred.promise; - } - - if ($routeParams.create) { - //we are creating so get an empty content item - contentResource.getScaffold($routeParams.id, $routeParams.doctype) - .then(function(data) { - $scope.loaded = true; - $scope.content = data; - configureButtons($scope.content); - }); - } - else { - //we are editing so get the content item from the server - contentResource.getById($routeParams.id) - .then(function(data) { - $scope.loaded = true; - $scope.content = data; - configureButtons($scope.content); - - //just get the cached version, no need to force a reload - navigationService.syncPath(data.path.split(","), false); - - //in one particular special case, after we've created a new item we redirect back to the edit - // route but there might be server validation errors in the collection which we need to display - // after the redirect, so we will bind all subscriptions which will show the server validation errors - // if there are any and then clear them so the collection no longer persists them. - serverValidationManager.executeAndClearAllSubscriptions(); - - - }); - } - - - $scope.unPublish = function () { - - if (formHelper.submitForm({ scope: $scope, statusMessage: "Unpublishing...", skipValidation: true })) { - - contentResource.unPublish($scope.content.id) - .then(function (data) { - - formHelper.resetForm({ scope: $scope, notifications: data.notifications }); - - contentEditingHelper.handleSuccessfulSave({ - scope: $scope, - newContent: data, - rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data) - }); - - configureButtons(data); - - navigationService.syncPath(data.path.split(","), true); - }); - } - - }; - - $scope.sendToPublish = function() { - return performSave({ saveMethod: contentResource.sendToPublish, statusMessage: "Sending..." }); - }; - - $scope.saveAndPublish = function() { - return performSave({ saveMethod: contentResource.publish, statusMessage: "Publishing..." }); - }; - - $scope.save = function () { - return performSave({ saveMethod: contentResource.save, statusMessage: "Saving..." }); - }; - - $scope.preview = function(content){ - if(!content.id){ - $scope.save().then(function(data){ - $window.open('dialogs/preview.aspx?id='+data.id,'umbpreview'); - }); - }else{ - $window.open('dialogs/preview.aspx?id='+content.id,'umbpreview'); - } - }; - - $scope.options = function(content){ - if(!content.id){ - return; - } - - if(!$scope.actions){ - treeService.getMenu({ treeNode: $scope.nav.ui.currentNode }) - .then(function(data) { - $scope.actions = data.menuItems; - }); - } - }; - - /** this method is called for all action buttons and then we proxy based on the btn definition */ - $scope.performAction = function(btn) { - if (!btn || !angular.isFunction(btn.handler)) { - throw "btn.handler must be a function reference"; - } - btn.handler.apply(this); - }; - -} - -angular.module("umbraco").controller("Umbraco.Editors.Content.EditController", ContentEditController); +/** + * @ngdoc controller + * @name Umbraco.Editors.Content.EditController + * @function + * + * @description + * The controller for the content editor + */ +function ContentEditController($scope, $routeParams, $q, $timeout, $window, contentResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, treeService, fileManager, formHelper, umbRequestHelper) { + + $scope.defaultButton = null; + $scope.subButtons = []; + $scope.nav = navigationService; + + //This sets up the action buttons based on what permissions the user has. + //The allowedActions parameter contains a list of chars, each represents a button by permission so + //here we'll build the buttons according to the chars of the user. + function configureButtons(content) { + //reset + $scope.subButtons = []; + + //This is the ideal button order but depends on circumstance, we'll use this array to create the button list + // Publish, SendToPublish, Save + var buttonOrder = ["U", "H", "A"]; + + //Create the first button (primary button) + //We cannot have the Save or SaveAndPublish buttons if they don't have create permissions when we are creating a new item. + if (!$routeParams.create || _.contains(content.allowedActions, "C")) { + for (var b in buttonOrder) { + if (_.contains(content.allowedActions, buttonOrder[b])) { + $scope.defaultButton = createButtonDefinition(buttonOrder[b]); + break; + } + } + } + + //Now we need to make the drop down button list, this is also slightly tricky because: + //We cannot have any buttons if there's no default button above. + //We cannot have the unpublish button (Z) when there's no publish permission. + //We cannot have the unpublish button (Z) when the item is not published. + if ($scope.defaultButton) { + + //get the last index of the button order + var lastIndex = _.indexOf(buttonOrder, $scope.defaultButton.letter); + //add the remaining + for (var i = lastIndex + 1; i < buttonOrder.length; i++) { + if (_.contains(content.allowedActions, buttonOrder[i])) { + $scope.subButtons.push(createButtonDefinition(buttonOrder[i])); + } + } + + //if we are not creating, then we should add unpublish too, + // so long as it's already published and if the user has access to publish + if (!$routeParams.create) { + if (content.publishDate && _.contains(content.allowedActions,"U")) { + $scope.subButtons.push(createButtonDefinition("Z")); + } + } + } + } + + function createButtonDefinition(ch) { + switch (ch) { + case "U": + //publish action + return { + letter: ch, + labelKey: "buttons_saveAndPublish", + handler: $scope.saveAndPublish, + hotKey: "ctrl+p" + }; + case "H": + //send to publish + return { + letter: ch, + labelKey: "buttons_saveToPublish", + handler: $scope.sendToPublish, + hotKey: "ctrl+t" + }; + case "A": + //save + return { + letter: ch, + labelKey: "buttons_save", + handler: $scope.save, + hotKey: "ctrl+s" + }; + case "Z": + //unpublish + return { + letter: ch, + labelKey: "content_unPublish", + handler: $scope.unPublish + }; + default: + return null; + } + } + + /** This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish */ + function performSave(args) { + var deferred = $q.defer(); + + if (formHelper.submitForm({ scope: $scope, statusMessage: args.statusMessage })) { + + args.saveMethod($scope.content, $routeParams.create, fileManager.getFiles()) + .then(function (data) { + + formHelper.resetForm({ scope: $scope, notifications: data.notifications }); + + contentEditingHelper.handleSuccessfulSave({ + scope: $scope, + newContent: data, + rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data) + }); + + configureButtons(data); + + navigationService.syncPath(data.path.split(","), true); + + deferred.resolve(data); + + }, function (err) { + + contentEditingHelper.handleSaveError({ + redirectOnFailure: true, + err: err, + allNewProps: contentEditingHelper.getAllProps(err.data), + allOrigProps: contentEditingHelper.getAllProps($scope.content) + }); + + deferred.reject(err); + }); + } + else { + deferred.reject(); + } + + return deferred.promise; + } + + if ($routeParams.create) { + //we are creating so get an empty content item + contentResource.getScaffold($routeParams.id, $routeParams.doctype) + .then(function(data) { + $scope.loaded = true; + $scope.content = data; + configureButtons($scope.content); + }); + } + else { + //we are editing so get the content item from the server + contentResource.getById($routeParams.id) + .then(function(data) { + $scope.loaded = true; + $scope.content = data; + configureButtons($scope.content); + + //just get the cached version, no need to force a reload + navigationService.syncPath(data.path.split(","), false); + + //in one particular special case, after we've created a new item we redirect back to the edit + // route but there might be server validation errors in the collection which we need to display + // after the redirect, so we will bind all subscriptions which will show the server validation errors + // if there are any and then clear them so the collection no longer persists them. + serverValidationManager.executeAndClearAllSubscriptions(); + + + }); + } + + + $scope.unPublish = function () { + + if (formHelper.submitForm({ scope: $scope, statusMessage: "Unpublishing...", skipValidation: true })) { + + contentResource.unPublish($scope.content.id) + .then(function (data) { + + formHelper.resetForm({ scope: $scope, notifications: data.notifications }); + + contentEditingHelper.handleSuccessfulSave({ + scope: $scope, + newContent: data, + rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data) + }); + + configureButtons(data); + + navigationService.syncPath(data.path.split(","), true); + }); + } + + }; + + $scope.sendToPublish = function() { + return performSave({ saveMethod: contentResource.sendToPublish, statusMessage: "Sending..." }); + }; + + $scope.saveAndPublish = function() { + return performSave({ saveMethod: contentResource.publish, statusMessage: "Publishing..." }); + }; + + $scope.save = function () { + return performSave({ saveMethod: contentResource.save, statusMessage: "Saving..." }); + }; + + $scope.preview = function(content){ + if(!content.id){ + $scope.save().then(function(data){ + $window.open('dialogs/preview.aspx?id='+data.id,'umbpreview'); + }); + }else{ + $window.open('dialogs/preview.aspx?id='+content.id,'umbpreview'); + } + }; + + $scope.options = function(content){ + if(!content.id){ + return; + } + + if(!$scope.actions){ + treeService.getMenu({ treeNode: $scope.nav.ui.currentNode }) + .then(function(data) { + $scope.actions = data.menuItems; + }); + } + }; + + /** this method is called for all action buttons and then we proxy based on the btn definition */ + $scope.performAction = function(btn) { + if (!btn || !angular.isFunction(btn.handler)) { + throw "btn.handler must be a function reference"; + } + btn.handler.apply(this); + }; + +} + +angular.module("umbraco").controller("Umbraco.Editors.Content.EditController", ContentEditController); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/copy.html b/src/Umbraco.Web.UI.Client/src/views/content/copy.html index 6b9df652e9..769347589f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/copy.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/copy.html @@ -1,49 +1,49 @@ -
    -
    -
    - -

    - Choose where to copy {{currentNode.name}} to in the tree struture below -

    - -
    -

    {{error.errorMsg}}

    -

    {{error.data.Message}}

    -
    - -
    -

    {{currentNode.name}} was copied to - {{target.name}}

    - - -
    - -
    - - - - - - - - - -
    -
    -
    - - - +
    +
    +
    + +

    + Choose where to copy {{currentNode.name}} to in the tree struture below +

    + +
    +

    {{error.errorMsg}}

    +

    {{error.data.Message}}

    +
    + +
    +

    {{currentNode.name}} was copied to + {{target.name}}

    + + +
    + +
    + + + + + + + + + +
    +
    +
    + + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/content/edit.html b/src/Umbraco.Web.UI.Client/src/views/content/edit.html index 2e699440e0..f7642001c5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/edit.html @@ -1,96 +1,91 @@ -
    - - - -
    - - - {{content.contentTypeName}} -
    - -
    - -
    -
    - -
    - - - -
    -
    -
    - - - - - - -
    -
    +
    + + + +
    + + + {{content.contentTypeName}} +
    + +
    + +
    +
    + +
    + + + +
    +
    +
    + + + + + + +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/content/move.html b/src/Umbraco.Web.UI.Client/src/views/content/move.html index 5456118374..fba9bc7e14 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/move.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/move.html @@ -1,38 +1,38 @@ -
    -
    -
    - -

    - Choose where to move {{currentNode.name}} to in the tree struture below -

    - -
    -

    {{error.errorMsg}}

    -

    {{error.data.Message}}

    -
    - -
    -

    {{currentNode.name}} was moved underneath - {{target.name}}

    - - -
    - -
    - - -
    -
    -
    - - - +
    +
    +
    + +

    + Choose where to move {{currentNode.name}} to in the tree struture below +

    + +
    +

    {{error.errorMsg}}

    +

    {{error.data.Message}}

    +
    + +
    +

    {{currentNode.name}} was moved underneath + {{target.name}}

    + + +
    + +
    + + +
    +
    +
    + + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/contenttype/contenttype.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/contenttype/contenttype.edit.controller.js index 2a871da0f6..55af70f287 100644 --- a/src/Umbraco.Web.UI.Client/src/views/contenttype/contenttype.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/contenttype/contenttype.edit.controller.js @@ -1,65 +1,65 @@ -/** - * @ngdoc controller - * @name Umbraco.Editors.ContentType.EditController - * @function - * - * @description - * The controller for the content type editor - */ -function ContentTypeEditController($scope, $routeParams, $log, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, entityResource) { - - $scope.tabs = []; - $scope.page = {}; - $scope.contentType = {tabs: [], name: "My content type", alias:"myType", icon:"icon-folder", allowedChildren: [], allowedTemplate: []}; - $scope.contentType.tabs = [ - {name: "Content", properties:[ {name: "test"}]}, - {name: "Generic Properties", properties:[]} - ]; - - - - $scope.dataTypesOptions ={ - group: "properties", - onDropHandler: function(item, args){ - args.sourceScope.move(args); - }, - onReleaseHandler: function(item, args){ - var a = args; - } - }; - - $scope.tabOptions ={ - group: "tabs", - drop: false, - nested: true, - onDropHandler: function(item, args){ - - }, - onReleaseHandler: function(item, args){ - - } - }; - - $scope.propertiesOptions ={ - group: "properties", - onDropHandler: function(item, args){ - //alert("dropped on properties"); - //args.targetScope.ngModel.$modelValue.push({name: "bong"}); - }, - onReleaseHandler: function(item, args){ - //alert("released from properties"); - //args.targetScope.ngModel.$modelValue.push({name: "bong"}); - }, - }; - - - $scope.omg = function(){ - alert("wat"); - }; - - entityResource.getAll("Datatype").then(function(data){ - $scope.page.datatypes = data; - }); -} - +/** + * @ngdoc controller + * @name Umbraco.Editors.ContentType.EditController + * @function + * + * @description + * The controller for the content type editor + */ +function ContentTypeEditController($scope, $routeParams, $log, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, entityResource) { + + $scope.tabs = []; + $scope.page = {}; + $scope.contentType = {tabs: [], name: "My content type", alias:"myType", icon:"icon-folder", allowedChildren: [], allowedTemplate: []}; + $scope.contentType.tabs = [ + {name: "Content", properties:[ {name: "test"}]}, + {name: "Generic Properties", properties:[]} + ]; + + + + $scope.dataTypesOptions ={ + group: "properties", + onDropHandler: function(item, args){ + args.sourceScope.move(args); + }, + onReleaseHandler: function(item, args){ + var a = args; + } + }; + + $scope.tabOptions ={ + group: "tabs", + drop: false, + nested: true, + onDropHandler: function(item, args){ + + }, + onReleaseHandler: function(item, args){ + + } + }; + + $scope.propertiesOptions ={ + group: "properties", + onDropHandler: function(item, args){ + //alert("dropped on properties"); + //args.targetScope.ngModel.$modelValue.push({name: "bong"}); + }, + onReleaseHandler: function(item, args){ + //alert("released from properties"); + //args.targetScope.ngModel.$modelValue.push({name: "bong"}); + }, + }; + + + $scope.omg = function(){ + alert("wat"); + }; + + entityResource.getAll("Datatype").then(function(data){ + $scope.page.datatypes = data; + }); +} + angular.module("umbraco").controller("Umbraco.Editors.ContentType.EditController", ContentTypeEditController); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/contenttype/edit.html b/src/Umbraco.Web.UI.Client/src/views/contenttype/edit.html index 82a4d7066a..b99e2bd3fd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/contenttype/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/contenttype/edit.html @@ -1,74 +1,74 @@ -
    - - - -
    - - -
    - -
    -
    - -
    - -
    -
    -
    -
    - - - -
    -
    - -
    -
      -
    • -
      {{tab.name}}
      - -
        -
      • - {{property.name}} -
      • -
      - -
    • -
    - - {{page.datatypes | json}} -
    - -
    - -
    -
    -
    -
    - - -
    - - - -
    -
    -
    -
    -
    +
    + + + +
    + + +
    + +
    +
    + +
    + +
    +
    +
    +
    + + + +
    +
    + +
    +
      +
    • +
      {{tab.name}}
      + +
        +
      • + {{property.name}} +
      • +
      + +
    • +
    + + {{page.datatypes | json}} +
    + +
    + +
    +
    +
    +
    + + +
    + + + +
    +
    +
    +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/ChangePassword.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/ChangePassword.html index 9d56075962..f35b501496 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/ChangePassword.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/ChangePassword.html @@ -1,18 +1,18 @@ -
    - -

    Change password

    -

    Enter your current password, then repeat your new password to change it

    - - - - - - - - - - -
    +
    + +

    Change password

    +

    Enter your current password, then repeat your new password to change it

    + + + + + + + + + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js index feb28eda13..4e0426f620 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js @@ -1,125 +1,125 @@ -function startUpVideosDashboardController($scope, xmlhelper, $log, $http) { - $scope.videos = []; - $scope.init = function(url){ - var proxyUrl = "dashboard/feedproxy.aspx?url=" + url; - $http.get(proxyUrl).then(function(data){ - var feed = $(data.data); - $('item', feed).each(function (i, item) { - var video = {}; - video.thumbnail = $(item).find('thumbnail').attr('url'); - video.title = $("title", item).text(); - video.link = $("guid", item).text(); - $scope.videos.push(video); - }); - }); - }; -} -angular.module("umbraco").controller("Umbraco.Dashboard.StartupVideosController", startUpVideosDashboardController); - -function startupLatestEditsController($scope) { - -} -angular.module("umbraco").controller("Umbraco.Dashboard.StartupLatestEditsController", startupLatestEditsController); - -function MediaFolderBrowserDashboardController($rootScope, $scope, assetsService, $routeParams, $timeout, $element, $location, umbRequestHelper, mediaResource, imageHelper) { - var dialogOptions = $scope.$parent.dialogOptions; - - $scope.filesUploading = []; - $scope.options = { - url: umbRequestHelper.getApiUrl("mediaApiBaseUrl", "PostAddFile"), - autoUpload: true, - disableImageResize: /Android(?!.*Chrome)|Opera/ - .test(window.navigator.userAgent), - previewMaxWidth: 200, - previewMaxHeight: 200, - previewCrop: true, - formData:{ - currentFolder: -1 - } - }; - - - $scope.loadChildren = function(){ - mediaResource.getChildren(-1) - .then(function(data) { - $scope.images = data.items; - }); - }; - - $scope.$on('fileuploadstop', function(event, files){ - $scope.loadChildren($scope.options.formData.currentFolder); - $scope.queue = []; - $scope.filesUploading = []; - }); - - $scope.$on('fileuploadprocessalways', function(e,data) { - var i; - $scope.$apply(function() { - $scope.filesUploading.push(data.files[data.index]); - }); - }); - - // All these sit-ups are to add dropzone area and make sure it gets removed if dragging is aborted! - $scope.$on('fileuploaddragover', function(event, files) { - if (!$scope.dragClearTimeout) { - $scope.$apply(function() { - $scope.dropping = true; - }); - } else { - $timeout.cancel($scope.dragClearTimeout); - } - $scope.dragClearTimeout = $timeout(function () { - $scope.dropping = null; - $scope.dragClearTimeout = null; - }, 300); - }); - - //init load - $scope.loadChildren(); -} -angular.module("umbraco").controller("Umbraco.Dashboard.MediaFolderBrowserDashboardController", MediaFolderBrowserDashboardController); - - -function ChangePasswordDashboardController($scope, xmlhelper, $log, userResource, formHelper) { - - //create the initial model for change password property editor - $scope.changePasswordModel = { - alias: "_umb_password", - view: "changepassword", - config: {}, - value: {} - }; - - //go get the config for the membership provider and add it to the model - userResource.getMembershipProviderConfig().then(function(data) { - $scope.changePasswordModel.config = data; - //ensure the hasPassword config option is set to true (the user of course has a password already assigned) - //this will ensure the oldPassword is shown so they can change it - $scope.changePasswordModel.config.hasPassword = true; - $scope.changePasswordModel.config.disableToggle = true; - }); - - ////this is the model we will pass to the service - //$scope.profile = {}; - - $scope.changePassword = function() { - - if (formHelper.submitForm({ scope: $scope })) { - userResource.changePassword($scope.changePasswordModel.value).then(function(data) { - - //if the password has been reset, then update our model - if (data.value) { - $scope.changePasswordModel.value.generatedPassword = data.value; - } - - formHelper.resetForm({ scope: $scope, notifications: data.notifications }); - - }, function (err) { - - formHelper.handleError(err); - - }); - } - }; -} +function startUpVideosDashboardController($scope, xmlhelper, $log, $http) { + $scope.videos = []; + $scope.init = function(url){ + var proxyUrl = "dashboard/feedproxy.aspx?url=" + url; + $http.get(proxyUrl).then(function(data){ + var feed = $(data.data); + $('item', feed).each(function (i, item) { + var video = {}; + video.thumbnail = $(item).find('thumbnail').attr('url'); + video.title = $("title", item).text(); + video.link = $("guid", item).text(); + $scope.videos.push(video); + }); + }); + }; +} +angular.module("umbraco").controller("Umbraco.Dashboard.StartupVideosController", startUpVideosDashboardController); + +function startupLatestEditsController($scope) { + +} +angular.module("umbraco").controller("Umbraco.Dashboard.StartupLatestEditsController", startupLatestEditsController); + +function MediaFolderBrowserDashboardController($rootScope, $scope, assetsService, $routeParams, $timeout, $element, $location, umbRequestHelper, mediaResource, imageHelper) { + var dialogOptions = $scope.$parent.dialogOptions; + + $scope.filesUploading = []; + $scope.options = { + url: umbRequestHelper.getApiUrl("mediaApiBaseUrl", "PostAddFile"), + autoUpload: true, + disableImageResize: /Android(?!.*Chrome)|Opera/ + .test(window.navigator.userAgent), + previewMaxWidth: 200, + previewMaxHeight: 200, + previewCrop: true, + formData:{ + currentFolder: -1 + } + }; + + + $scope.loadChildren = function(){ + mediaResource.getChildren(-1) + .then(function(data) { + $scope.images = data.items; + }); + }; + + $scope.$on('fileuploadstop', function(event, files){ + $scope.loadChildren($scope.options.formData.currentFolder); + $scope.queue = []; + $scope.filesUploading = []; + }); + + $scope.$on('fileuploadprocessalways', function(e,data) { + var i; + $scope.$apply(function() { + $scope.filesUploading.push(data.files[data.index]); + }); + }); + + // All these sit-ups are to add dropzone area and make sure it gets removed if dragging is aborted! + $scope.$on('fileuploaddragover', function(event, files) { + if (!$scope.dragClearTimeout) { + $scope.$apply(function() { + $scope.dropping = true; + }); + } else { + $timeout.cancel($scope.dragClearTimeout); + } + $scope.dragClearTimeout = $timeout(function () { + $scope.dropping = null; + $scope.dragClearTimeout = null; + }, 300); + }); + + //init load + $scope.loadChildren(); +} +angular.module("umbraco").controller("Umbraco.Dashboard.MediaFolderBrowserDashboardController", MediaFolderBrowserDashboardController); + + +function ChangePasswordDashboardController($scope, xmlhelper, $log, userResource, formHelper) { + + //create the initial model for change password property editor + $scope.changePasswordModel = { + alias: "_umb_password", + view: "changepassword", + config: {}, + value: {} + }; + + //go get the config for the membership provider and add it to the model + userResource.getMembershipProviderConfig().then(function(data) { + $scope.changePasswordModel.config = data; + //ensure the hasPassword config option is set to true (the user of course has a password already assigned) + //this will ensure the oldPassword is shown so they can change it + $scope.changePasswordModel.config.hasPassword = true; + $scope.changePasswordModel.config.disableToggle = true; + }); + + ////this is the model we will pass to the service + //$scope.profile = {}; + + $scope.changePassword = function() { + + if (formHelper.submitForm({ scope: $scope })) { + userResource.changePassword($scope.changePasswordModel.value).then(function(data) { + + //if the password has been reset, then update our model + if (data.value) { + $scope.changePasswordModel.value.generatedPassword = data.value; + } + + formHelper.resetForm({ scope: $scope, notifications: data.notifications }); + + }, function (err) { + + formHelper.handleError(err); + + }); + } + }; +} angular.module("umbraco").controller("Umbraco.Dashboard.StartupChangePasswordController", ChangePasswordDashboardController); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardVideos.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardVideos.html index 2135c37859..ec55790bd9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardVideos.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardVideos.html @@ -1,18 +1,18 @@ -

    Hours of Umbraco training videos are only a click away

    -

    Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos

    - -

    To get you started:

    -
    - - +

    Hours of Umbraco training videos are only a click away

    +

    Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos

    + +

    To get you started:

    +
    + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/developerdashboardvideos.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/developerdashboardvideos.html index 0e3c107cdc..e0851e375e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/developerdashboardvideos.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/developerdashboardvideos.html @@ -1,18 +1,18 @@ -

    Hours of Umbraco training videos are only a click away

    -

    Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos

    - -

    To get you started:

    -
    - - +

    Hours of Umbraco training videos are only a click away

    +

    Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos

    + +

    To get you started:

    +
    + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/media/mediadashboardvideos.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/media/mediadashboardvideos.html index 2135c37859..ec55790bd9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/media/mediadashboardvideos.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/media/mediadashboardvideos.html @@ -1,18 +1,18 @@ -

    Hours of Umbraco training videos are only a click away

    -

    Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos

    - -

    To get you started:

    -
    - - +

    Hours of Umbraco training videos are only a click away

    +

    Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos

    + +

    To get you started:

    +
    + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardvideos.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardvideos.html index 2135c37859..ec55790bd9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardvideos.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardvideos.html @@ -1,18 +1,18 @@ -

    Hours of Umbraco training videos are only a click away

    -

    Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos

    - -

    To get you started:

    -
    - - +

    Hours of Umbraco training videos are only a click away

    +

    Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos

    + +

    To get you started:

    +
    + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/datatype/edit.html b/src/Umbraco.Web.UI.Client/src/views/datatype/edit.html index 5f8fe20c04..6bcb06249d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatype/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatype/edit.html @@ -1,87 +1,87 @@ -
    - - - - -
    - -
    - -
    - -
    -
    - -
    -
    -
    - - -
    - - Required -
    - -
    - - -
    {{content.selectedEditor}}
    -
    - -
    - - - - - - - -
    -
    - -
    -
    - -
    -
    -
    - -
    -
    +
    + + + + +
    + +
    + +
    + +
    +
    + +
    +
    +
    + + +
    + + Required +
    + +
    + + +
    {{content.selectedEditor}}
    +
    + +
    + + + + + + + +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/html/umb-control-group.html b/src/Umbraco.Web.UI.Client/src/views/directives/html/umb-control-group.html index ce8028d70a..7284e96cc4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/directives/html/umb-control-group.html +++ b/src/Umbraco.Web.UI.Client/src/views/directives/html/umb-control-group.html @@ -1,13 +1,13 @@ -
    -
    -
    - - -
    -
    -
    -
    +
    +
    +
    + + +
    +
    +
    +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/html/umb-pane.html b/src/Umbraco.Web.UI.Client/src/views/directives/html/umb-pane.html index 7c94769d2d..b9cda6bb26 100644 --- a/src/Umbraco.Web.UI.Client/src/views/directives/html/umb-pane.html +++ b/src/Umbraco.Web.UI.Client/src/views/directives/html/umb-pane.html @@ -1,3 +1,3 @@ -
    - +
    +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/html/umb-panel.html b/src/Umbraco.Web.UI.Client/src/views/directives/html/umb-panel.html index a963eff309..62460be6d5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/directives/html/umb-panel.html +++ b/src/Umbraco.Web.UI.Client/src/views/directives/html/umb-panel.html @@ -1,3 +1,3 @@ -
    - -
    +
    + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/html/umb-upload-dropzone.html b/src/Umbraco.Web.UI.Client/src/views/directives/html/umb-upload-dropzone.html index 38e511ce7a..728c4ac526 100644 --- a/src/Umbraco.Web.UI.Client/src/views/directives/html/umb-upload-dropzone.html +++ b/src/Umbraco.Web.UI.Client/src/views/directives/html/umb-upload-dropzone.html @@ -1,11 +1,11 @@ -
    -
    - -

    Drop your files here...

    -
    - -
    -
    -
    -
    +
    +
    + +

    Drop your files here...

    +
    + +
    +
    +
    +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/media/move.html b/src/Umbraco.Web.UI.Client/src/views/media/move.html index 9c0c74a72e..eb84a48b7f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/move.html +++ b/src/Umbraco.Web.UI.Client/src/views/media/move.html @@ -1,37 +1,37 @@ -
    -
    -
    - -

    - Choose where to move {{currentNode.name}} to in the tree struture below -

    - -
    -

    {{error.errorMsg}}

    -

    {{error.data.Message}}

    -
    - -
    -

    {{currentNode.name}} was moved underneath - {{target.name}}

    - - -
    - -
    - - -
    -
    -
    - - - +
    +
    +
    + +

    + Choose where to move {{currentNode.name}} to in the tree struture below +

    + +
    +

    {{error.errorMsg}}

    +

    {{error.data.Message}}

    +
    + +
    +

    {{currentNode.name}} was moved underneath + {{target.name}}

    + + +
    + +
    + + +
    +
    +
    + + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/member/create.html b/src/Umbraco.Web.UI.Client/src/views/member/create.html index a44369daa7..9dcd7ff836 100644 --- a/src/Umbraco.Web.UI.Client/src/views/member/create.html +++ b/src/Umbraco.Web.UI.Client/src/views/member/create.html @@ -1,25 +1,25 @@ - - - - + + + + diff --git a/src/Umbraco.Web.UI.Client/src/views/member/delete.html b/src/Umbraco.Web.UI.Client/src/views/member/delete.html index c4d931b53b..e701d8cddc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/member/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/member/delete.html @@ -1,12 +1,12 @@ -
    -
    - -

    - Are you sure you want to delete {{currentNode.name}} ? -

    - - - - -
    -
    +
    +
    + +

    + Are you sure you want to delete {{currentNode.name}} ? +

    + + + + +
    +
    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 f9057def5a..f007d2176d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/member/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/member/edit.html @@ -1,70 +1,70 @@ -
    - - - -
    - -
    - -
    -
    -
    - -
    - - -
    -
    -
    - - - -
    - - - - - -
    -
    - -
    -
    -
    -
    - -
    -
    + + + + +
    + +
    + +
    +
    +
    + +
    + + +
    +
    +
    + + + +
    + + + + + +
    +
    + +
    +
    +
    +
    + +
    +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/member/member.create.controller.js b/src/Umbraco.Web.UI.Client/src/views/member/member.create.controller.js index adbf4dfd7f..636f4f4f4d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/member/member.create.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/member/member.create.controller.js @@ -1,17 +1,17 @@ -/** - * @ngdoc controller - * @name Umbraco.Editors.Member.CreateController - * @function - * - * @description - * The controller for the member creation dialog - */ -function memberCreateController($scope, $routeParams, memberTypeResource, iconHelper) { - - memberTypeResource.getTypes($scope.currentNode.id).then(function (data) { - $scope.allowedTypes = iconHelper.formatContentTypeIcons(data); - }); - -} - +/** + * @ngdoc controller + * @name Umbraco.Editors.Member.CreateController + * @function + * + * @description + * The controller for the member creation dialog + */ +function memberCreateController($scope, $routeParams, memberTypeResource, iconHelper) { + + memberTypeResource.getTypes($scope.currentNode.id).then(function (data) { + $scope.allowedTypes = iconHelper.formatContentTypeIcons(data); + }); + +} + angular.module('umbraco').controller("Umbraco.Editors.Member.CreateController", memberCreateController); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/member/member.delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/member/member.delete.controller.js index dc86b21731..07945e2ef5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/member/member.delete.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/member/member.delete.controller.js @@ -1,32 +1,32 @@ -/** - * @ngdoc controller - * @name Umbraco.Editors.Member.DeleteController - * @function - * - * @description - * The controller for deleting content - */ -function MemberDeleteController($scope, memberResource, treeService, navigationService) { - - $scope.performDelete = function() { - - //mark it for deletion (used in the UI) - $scope.currentNode.loading = true; - - memberResource.deleteByKey($scope.currentNode.id).then(function () { - $scope.currentNode.loading = false; - - //TODO: Need to sync tree, etc... - treeService.removeNode($scope.currentNode); - - navigationService.hideMenu(); - }); - - }; - - $scope.cancel = function() { - navigationService.hideDialog(); - }; -} - -angular.module("umbraco").controller("Umbraco.Editors.Member.DeleteController", MemberDeleteController); +/** + * @ngdoc controller + * @name Umbraco.Editors.Member.DeleteController + * @function + * + * @description + * The controller for deleting content + */ +function MemberDeleteController($scope, memberResource, treeService, navigationService) { + + $scope.performDelete = function() { + + //mark it for deletion (used in the UI) + $scope.currentNode.loading = true; + + memberResource.deleteByKey($scope.currentNode.id).then(function () { + $scope.currentNode.loading = false; + + //TODO: Need to sync tree, etc... + treeService.removeNode($scope.currentNode); + + navigationService.hideMenu(); + }); + + }; + + $scope.cancel = function() { + navigationService.hideDialog(); + }; +} + +angular.module("umbraco").controller("Umbraco.Editors.Member.DeleteController", MemberDeleteController); 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 35e359a4a2..20f1abc007 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 @@ -1,106 +1,106 @@ -/** - * @ngdoc controller - * @name Umbraco.Editors.Member.EditController - * @function - * - * @description - * The controller for the member editor - */ -function MemberEditController($scope, $routeParams, $location, $q, $window, memberResource, entityResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, fileManager, formHelper, treeService) { - - $scope.nav = navigationService; - - - if ($routeParams.create) { - //we are creating so get an empty member item - memberResource.getScaffold($routeParams.doctype) - .then(function(data) { - $scope.loaded = true; - $scope.content = data; - }); - } - else { - //so, we usually refernce all editors with the Int ID, but with members we have - //a different pattern, adding a route-redirect here to handle this: - //isNumber doesnt work here since its seen as a string - - //TODO: Why is this here - I don't understand why this would ever be an integer? This will not work when we support non-umbraco membership providers. - - if ($routeParams.id && $routeParams.id.length < 9) { - entityResource.getById($routeParams.id, "Member").then(function(entity) { - $location.path("member/member/edit/" + entity.key); - }); - } - else { - //we are editing so get the content item from the server - memberResource.getByKey($routeParams.id) - .then(function(data) { - $scope.loaded = true; - $scope.content = data; - - //build a path to sync the tree with - var path = data.name[0]+"," + data.key; - path = path.replace(/-/g,''); - - navigationService.setActiveTreeType("member"); - navigationService.syncPath(path.split(","), true); - - //in one particular special case, after we've created a new item we redirect back to the edit - // route but there might be server validation errors in the collection which we need to display - // after the redirect, so we will bind all subscriptions which will show the server validation errors - // if there are any and then clear them so the collection no longer persists them. - serverValidationManager.executeAndClearAllSubscriptions(); - }); - } - - } - - - - $scope.options = function(content){ - if(!content.id){ - return; - } - - if(!$scope.actions){ - treeService.getMenu({ treeNode: $scope.nav.ui.currentNode }) - .then(function(data) { - $scope.actions = data.menuItems; - }); - } - }; - - $scope.save = function() { - - if (formHelper.submitForm({ scope: $scope, statusMessage: "Saving..." })) { - - memberResource.save($scope.content, $routeParams.create, fileManager.getFiles()) - .then(function(data) { - - formHelper.resetForm({ scope: $scope, notifications: data.notifications }); - - contentEditingHelper.handleSuccessfulSave({ - scope: $scope, - newContent: data, - //specify a custom id to redirect to since we want to use the GUID - redirectId: data.key, - rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data) - }); - - }, function (err) { - - contentEditingHelper.handleSaveError({ - redirectOnFailure: false, - err: err, - allNewProps: contentEditingHelper.getAllProps(err.data), - allOrigProps: contentEditingHelper.getAllProps($scope.content) - }); - - }); - } - - }; - -} - -angular.module("umbraco").controller("Umbraco.Editors.Member.EditController", MemberEditController); +/** + * @ngdoc controller + * @name Umbraco.Editors.Member.EditController + * @function + * + * @description + * The controller for the member editor + */ +function MemberEditController($scope, $routeParams, $location, $q, $window, memberResource, entityResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, fileManager, formHelper, treeService) { + + $scope.nav = navigationService; + + + if ($routeParams.create) { + //we are creating so get an empty member item + memberResource.getScaffold($routeParams.doctype) + .then(function(data) { + $scope.loaded = true; + $scope.content = data; + }); + } + else { + //so, we usually refernce all editors with the Int ID, but with members we have + //a different pattern, adding a route-redirect here to handle this: + //isNumber doesnt work here since its seen as a string + + //TODO: Why is this here - I don't understand why this would ever be an integer? This will not work when we support non-umbraco membership providers. + + if ($routeParams.id && $routeParams.id.length < 9) { + entityResource.getById($routeParams.id, "Member").then(function(entity) { + $location.path("member/member/edit/" + entity.key); + }); + } + else { + //we are editing so get the content item from the server + memberResource.getByKey($routeParams.id) + .then(function(data) { + $scope.loaded = true; + $scope.content = data; + + //build a path to sync the tree with + var path = data.name[0]+"," + data.key; + path = path.replace(/-/g,''); + + navigationService.setActiveTreeType("member"); + navigationService.syncPath(path.split(","), true); + + //in one particular special case, after we've created a new item we redirect back to the edit + // route but there might be server validation errors in the collection which we need to display + // after the redirect, so we will bind all subscriptions which will show the server validation errors + // if there are any and then clear them so the collection no longer persists them. + serverValidationManager.executeAndClearAllSubscriptions(); + }); + } + + } + + + + $scope.options = function(content){ + if(!content.id){ + return; + } + + if(!$scope.actions){ + treeService.getMenu({ treeNode: $scope.nav.ui.currentNode }) + .then(function(data) { + $scope.actions = data.menuItems; + }); + } + }; + + $scope.save = function() { + + if (formHelper.submitForm({ scope: $scope, statusMessage: "Saving..." })) { + + memberResource.save($scope.content, $routeParams.create, fileManager.getFiles()) + .then(function(data) { + + formHelper.resetForm({ scope: $scope, notifications: data.notifications }); + + contentEditingHelper.handleSuccessfulSave({ + scope: $scope, + newContent: data, + //specify a custom id to redirect to since we want to use the GUID + redirectId: data.key, + rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data) + }); + + }, function (err) { + + contentEditingHelper.handleSaveError({ + redirectOnFailure: false, + err: err, + allNewProps: contentEditingHelper.getAllProps(err.data), + allOrigProps: contentEditingHelper.getAllProps($scope.content) + }); + + }); + } + + }; + +} + +angular.module("umbraco").controller("Umbraco.Editors.Member.EditController", MemberEditController); diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/number.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/number.html index 2d3a25e9fc..6d7b8c4c78 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/number.html +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/number.html @@ -1,11 +1,11 @@ -
    - - - Not a number - {{propertyForm.requiredField.errorMsg}} - +
    + + + Not a number + {{propertyForm.requiredField.errorMsg}} +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/readonlykeyvalues.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/readonlykeyvalues.html index a9a55588d4..66faad24d1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/readonlykeyvalues.html +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/readonlykeyvalues.html @@ -1,7 +1,7 @@ -
    -
      -
    • - {{preVal.Key}} : {{preVal.Value}} -
    • -
    +
    +
      +
    • + {{preVal.Key}} : {{preVal.Value}} +
    • +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treepicker.controller.js index ff92c0a35f..4a54378ebf 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treepicker.controller.js @@ -1,80 +1,80 @@ -//this controller simply tells the dialogs service to open a mediaPicker window -//with a specified callback, this callback will receive an object with a selection on it -angular.module('umbraco') -.controller("Umbraco.PrevalueEditors.TreePickerController", - - function($scope, dialogService, entityResource, $log, iconHelper){ - $scope.renderModel = []; - $scope.ids = []; - - - $scope.cfg = { - multiPicker: false, - entityType: "Document", - type: "content", - treeAlias: "content" - }; - - if($scope.model.value){ - $scope.ids = $scope.model.value.split(','); - entityResource.getByIds($scope.ids, $scope.cfg.entityType).then(function(data){ - _.each(data, function (item, i) { - item.icon = iconHelper.convertFromLegacyIcon(item.icon); - $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}); - }); - }); - } - - - $scope.openContentPicker =function(){ - var d = dialogService.treePicker({ - section: $scope.cfg.type, - treeAlias: $scope.cfg.type, - scope: $scope, - multiPicker: $scope.cfg.multiPicker, - callback: populate}); - }; - - $scope.remove =function(index){ - $scope.renderModel.splice(index, 1); - $scope.ids.splice(index, 1); - $scope.model.value = trim($scope.ids.join(), ","); - }; - - $scope.clear = function() { - $scope.model.value = ""; - $scope.renderModel = []; - $scope.ids = []; - }; - - $scope.add =function(item){ - if($scope.ids.indexOf(item.id) < 0){ - item.icon = iconHelper.convertFromLegacyIcon(item.icon); - - $scope.ids.push(item.id); - $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}); - $scope.model.value = trim($scope.ids.join(), ","); - } - }; - - - $scope.$on("formSubmitting", function (ev, args) { - $scope.model.value = trim($scope.ids.join(), ","); - }); - - function trim(str, chr) { - var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^'+chr+'+|'+chr+'+$', 'g'); - return str.replace(rgxtrim, ''); - } - - function populate(data){ - if(angular.isArray(data)){ - _.each(data, function (item, i) { - $scope.add(item); - }); - }else{ - $scope.clear(); - $scope.add(data); - } - } +//this controller simply tells the dialogs service to open a mediaPicker window +//with a specified callback, this callback will receive an object with a selection on it +angular.module('umbraco') +.controller("Umbraco.PrevalueEditors.TreePickerController", + + function($scope, dialogService, entityResource, $log, iconHelper){ + $scope.renderModel = []; + $scope.ids = []; + + + $scope.cfg = { + multiPicker: false, + entityType: "Document", + type: "content", + treeAlias: "content" + }; + + if($scope.model.value){ + $scope.ids = $scope.model.value.split(','); + entityResource.getByIds($scope.ids, $scope.cfg.entityType).then(function(data){ + _.each(data, function (item, i) { + item.icon = iconHelper.convertFromLegacyIcon(item.icon); + $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}); + }); + }); + } + + + $scope.openContentPicker =function(){ + var d = dialogService.treePicker({ + section: $scope.cfg.type, + treeAlias: $scope.cfg.type, + scope: $scope, + multiPicker: $scope.cfg.multiPicker, + callback: populate}); + }; + + $scope.remove =function(index){ + $scope.renderModel.splice(index, 1); + $scope.ids.splice(index, 1); + $scope.model.value = trim($scope.ids.join(), ","); + }; + + $scope.clear = function() { + $scope.model.value = ""; + $scope.renderModel = []; + $scope.ids = []; + }; + + $scope.add =function(item){ + if($scope.ids.indexOf(item.id) < 0){ + item.icon = iconHelper.convertFromLegacyIcon(item.icon); + + $scope.ids.push(item.id); + $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}); + $scope.model.value = trim($scope.ids.join(), ","); + } + }; + + + $scope.$on("formSubmitting", function (ev, args) { + $scope.model.value = trim($scope.ids.join(), ","); + }); + + function trim(str, chr) { + var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^'+chr+'+|'+chr+'+$', 'g'); + return str.replace(rgxtrim, ''); + } + + function populate(data){ + if(angular.isArray(data)){ + _.each(data, function (item, i) { + $scope.add(item); + }); + }else{ + $scope.clear(); + $scope.add(data); + } + } }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treepicker.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treepicker.html index 47270dda75..71ce43b883 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treepicker.html @@ -1,22 +1,22 @@ -
    - - - - +
    + + + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/changepassword/changepassword.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/changepassword/changepassword.controller.js index b6a5c8ee2a..8ae6b137d6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/changepassword/changepassword.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/changepassword/changepassword.controller.js @@ -1,135 +1,135 @@ -angular.module("umbraco").controller("Umbraco.PropertyEditors.ChangePasswordController", - function ($scope, $routeParams) { - - function resetModel(isNew) { - //the model config will contain an object, if it does not we'll create defaults - //NOTE: We will not support doing the password regex on the client side because the regex on the server side - //based on the membership provider cannot always be ported to js from .net directly. - /* - { - hasPassword: true/false, - requiresQuestionAnswer: true/false, - enableReset: true/false, - enablePasswordRetrieval: true/false, - minPasswordLength: 10 - } - */ - - //set defaults if they are not available - if (!$scope.model.config || $scope.model.config.disableToggle === undefined) { - $scope.model.config.disableToggle = false; - } - if (!$scope.model.config || $scope.model.config.hasPassword === undefined) { - $scope.model.config.hasPassword = false; - } - if (!$scope.model.config || $scope.model.config.enablePasswordRetrieval === undefined) { - $scope.model.config.enablePasswordRetrieval = true; - } - if (!$scope.model.config || $scope.model.config.requiresQuestionAnswer === undefined) { - $scope.model.config.requiresQuestionAnswer = false; - } - if (!$scope.model.config || $scope.model.config.enableReset === undefined) { - $scope.model.config.enableReset = true; - } - if (!$scope.model.config || $scope.model.config.minPasswordLength === undefined) { - $scope.model.config.minPasswordLength = 0; - } - - //set the model defaults - if (!angular.isObject($scope.model.value)) { - //if it's not an object then just create a new one - $scope.model.value = { - newPassword: null, - oldPassword: null, - reset: null, - answer: null - }; - } - else { - //just reset the values - - if (!isNew) { - //if it is new, then leave the generated pass displayed - $scope.model.value.newPassword = null; - $scope.model.value.oldPassword = null; - } - $scope.model.value.reset = null; - $scope.model.value.answer = null; - } - - //the value to compare to match passwords - if (!isNew) { - $scope.model.confirm = ""; - } - else if ($scope.model.value.newPassword.length > 0) { - //if it is new and a new password has been set, then set the confirm password too - $scope.model.confirm = $scope.model.value.newPassword; - } - - } - - resetModel($routeParams.create); - - //if there is no password saved for this entity , it must be new so we do not allow toggling of the change password, it is always there - //with validators turned on. - $scope.changing = $scope.model.config.disableToggle === true || !$scope.model.config.hasPassword; - - //we're not currently changing so set the model to null - if (!$scope.changing) { - $scope.model.value = null; - } - - $scope.doChange = function() { - resetModel(); - $scope.changing = true; - //if there was a previously generated password displaying, clear it - $scope.model.value.generatedPassword = null; - }; - - $scope.cancelChange = function() { - $scope.changing = false; - //set model to null - $scope.model.value = null; - }; - - //listen for the saved event, when that occurs we'll - //change to changing = false; - $scope.$on("formSubmitted", function () { - if ($scope.model.config.disableToggle === false) { - $scope.changing = false; - } - }); - $scope.$on("formSubmitting", function() { - //if there was a previously generated password displaying, clear it - if ($scope.changing && $scope.model.value) { - $scope.model.value.generatedPassword = null; - } - else if (!$scope.changing) { - //we are not changing, so the model needs to be null - $scope.model.value = null; - } - }); - - $scope.showReset = function() { - return $scope.model.config.hasPassword && $scope.model.config.enableReset; - }; - - $scope.showOldPass = function() { - return $scope.model.config.hasPassword && - !$scope.model.config.allowManuallyChangingPassword && - !$scope.model.config.enablePasswordRetrieval && !$scope.model.value.reset; - }; - - $scope.showNewPass = function () { - return !$scope.model.value.reset; - }; - - $scope.showConfirmPass = function() { - return !$scope.model.value.reset; - }; - - $scope.showCancelBtn = function() { - return $scope.model.config.disableToggle !== true && $scope.model.config.hasPassword; - }; - - }); +angular.module("umbraco").controller("Umbraco.PropertyEditors.ChangePasswordController", + function ($scope, $routeParams) { + + function resetModel(isNew) { + //the model config will contain an object, if it does not we'll create defaults + //NOTE: We will not support doing the password regex on the client side because the regex on the server side + //based on the membership provider cannot always be ported to js from .net directly. + /* + { + hasPassword: true/false, + requiresQuestionAnswer: true/false, + enableReset: true/false, + enablePasswordRetrieval: true/false, + minPasswordLength: 10 + } + */ + + //set defaults if they are not available + if (!$scope.model.config || $scope.model.config.disableToggle === undefined) { + $scope.model.config.disableToggle = false; + } + if (!$scope.model.config || $scope.model.config.hasPassword === undefined) { + $scope.model.config.hasPassword = false; + } + if (!$scope.model.config || $scope.model.config.enablePasswordRetrieval === undefined) { + $scope.model.config.enablePasswordRetrieval = true; + } + if (!$scope.model.config || $scope.model.config.requiresQuestionAnswer === undefined) { + $scope.model.config.requiresQuestionAnswer = false; + } + if (!$scope.model.config || $scope.model.config.enableReset === undefined) { + $scope.model.config.enableReset = true; + } + if (!$scope.model.config || $scope.model.config.minPasswordLength === undefined) { + $scope.model.config.minPasswordLength = 0; + } + + //set the model defaults + if (!angular.isObject($scope.model.value)) { + //if it's not an object then just create a new one + $scope.model.value = { + newPassword: null, + oldPassword: null, + reset: null, + answer: null + }; + } + else { + //just reset the values + + if (!isNew) { + //if it is new, then leave the generated pass displayed + $scope.model.value.newPassword = null; + $scope.model.value.oldPassword = null; + } + $scope.model.value.reset = null; + $scope.model.value.answer = null; + } + + //the value to compare to match passwords + if (!isNew) { + $scope.model.confirm = ""; + } + else if ($scope.model.value.newPassword.length > 0) { + //if it is new and a new password has been set, then set the confirm password too + $scope.model.confirm = $scope.model.value.newPassword; + } + + } + + resetModel($routeParams.create); + + //if there is no password saved for this entity , it must be new so we do not allow toggling of the change password, it is always there + //with validators turned on. + $scope.changing = $scope.model.config.disableToggle === true || !$scope.model.config.hasPassword; + + //we're not currently changing so set the model to null + if (!$scope.changing) { + $scope.model.value = null; + } + + $scope.doChange = function() { + resetModel(); + $scope.changing = true; + //if there was a previously generated password displaying, clear it + $scope.model.value.generatedPassword = null; + }; + + $scope.cancelChange = function() { + $scope.changing = false; + //set model to null + $scope.model.value = null; + }; + + //listen for the saved event, when that occurs we'll + //change to changing = false; + $scope.$on("formSubmitted", function () { + if ($scope.model.config.disableToggle === false) { + $scope.changing = false; + } + }); + $scope.$on("formSubmitting", function() { + //if there was a previously generated password displaying, clear it + if ($scope.changing && $scope.model.value) { + $scope.model.value.generatedPassword = null; + } + else if (!$scope.changing) { + //we are not changing, so the model needs to be null + $scope.model.value = null; + } + }); + + $scope.showReset = function() { + return $scope.model.config.hasPassword && $scope.model.config.enableReset; + }; + + $scope.showOldPass = function() { + return $scope.model.config.hasPassword && + !$scope.model.config.allowManuallyChangingPassword && + !$scope.model.config.enablePasswordRetrieval && !$scope.model.value.reset; + }; + + $scope.showNewPass = function () { + return !$scope.model.value.reset; + }; + + $scope.showConfirmPass = function() { + return !$scope.model.value.reset; + }; + + $scope.showCancelBtn = function() { + return $scope.model.config.disableToggle !== true && $scope.model.config.hasPassword; + }; + + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/changepassword/changepassword.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/changepassword/changepassword.html index 0dbb234acd..baea038562 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/changepassword/changepassword.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/changepassword/changepassword.html @@ -1,53 +1,53 @@ -
    -
    - Password has been reset to: -
    - {{model.value.generatedPassword}} -
    -
    - -
    - - - - - - - - - - Required - - - - - - Required - Minimum {{$parent.model.config.minPasswordLength}} characters - - - - - - - Passwords must match - - - Cancel -
    -
    +
    +
    + Password has been reset to: +
    + {{model.value.generatedPassword}} +
    +
    + +
    + + + + + + + + + + Required + + + + + + Required + Minimum {{$parent.model.config.minPasswordLength}} characters + + + + + + + Passwords must match + + + Cancel +
    +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html index 635717be99..f31b16a6da 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/email/email.html @@ -1,13 +1,13 @@ -
    - - - - Required - Invalid email - -
    +
    + + + + Required + Invalid email + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/entitypicker/entitypicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/entitypicker/entitypicker.controller.js index be338389ad..48d122c9b2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/entitypicker/entitypicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/entitypicker/entitypicker.controller.js @@ -1,40 +1,40 @@ -/** A drop down list or multi value select list based on an entity type, this can be re-used for any entity types */ -function entityPicker($scope, entityResource) { - - //set the default to DocumentType - if (!$scope.model.config.entityType) { - $scope.model.config.entityType = "DocumentType"; - } - - //Determine the select list options and which value to publish - if (!$scope.model.config.publishBy) { - $scope.selectOptions = "entity.id as entity.name for entity in entities"; - } - else { - $scope.selectOptions = "entity." + $scope.model.config.publishBy + " as entity.name for entity in entities"; - } - - entityResource.getAll($scope.model.config.entityType).then(function (data) { - //convert the ids to strings so the drop downs work properly when comparing - _.each(data, function(d) { - d.id = d.id.toString(); - }); - $scope.entities = data; - }); - - if ($scope.model.value === null || $scope.model.value === undefined) { - if ($scope.model.config.multiple) { - $scope.model.value = []; - } - else { - $scope.model.value = ""; - } - } - else { - //if it's multiple, change the value to an array - if ($scope.model.config.multiple === "1") { - $scope.model.value = $scope.model.value.split(','); - } - } -} +/** A drop down list or multi value select list based on an entity type, this can be re-used for any entity types */ +function entityPicker($scope, entityResource) { + + //set the default to DocumentType + if (!$scope.model.config.entityType) { + $scope.model.config.entityType = "DocumentType"; + } + + //Determine the select list options and which value to publish + if (!$scope.model.config.publishBy) { + $scope.selectOptions = "entity.id as entity.name for entity in entities"; + } + else { + $scope.selectOptions = "entity." + $scope.model.config.publishBy + " as entity.name for entity in entities"; + } + + entityResource.getAll($scope.model.config.entityType).then(function (data) { + //convert the ids to strings so the drop downs work properly when comparing + _.each(data, function(d) { + d.id = d.id.toString(); + }); + $scope.entities = data; + }); + + if ($scope.model.value === null || $scope.model.value === undefined) { + if ($scope.model.config.multiple) { + $scope.model.value = []; + } + else { + $scope.model.value = ""; + } + } + else { + //if it's multiple, change the value to an array + if ($scope.model.config.multiple === "1") { + $scope.model.value = $scope.model.value.split(','); + } + } +} angular.module('umbraco').controller("Umbraco.PropertyEditors.EntityPickerController", entityPicker); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/entitypicker/entitypicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/entitypicker/entitypicker.html index 274710b14e..3440843928 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/entitypicker/entitypicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/entitypicker/entitypicker.html @@ -1,19 +1,19 @@ -
    - - - - - - -
    +
    + + + + + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/folderbrowser/folderbrowser.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/folderbrowser/folderbrowser.controller.js index 9c81f829ad..7ee81e7cd9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/folderbrowser/folderbrowser.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/folderbrowser/folderbrowser.controller.js @@ -1,74 +1,74 @@ -angular.module("umbraco") -.directive("umbUploadPreview",function($parse){ - return { - link: function(scope, element, attr, ctrl) { - var fn = $parse(attr.umbUploadPreview), - file = fn(scope); - if (file.preview) { - element.append(file.preview); - } - } - }; -}) -.controller("Umbraco.PropertyEditors.FolderBrowserController", - function ($rootScope, $scope, assetsService, $routeParams, $timeout, $element, $location, umbRequestHelper, mediaResource, imageHelper) { - var dialogOptions = $scope.$parent.dialogOptions; - - $scope.creating = $routeParams.create; - - if(!$scope.creating){ - - $scope.filesUploading = []; - $scope.options = { - url: umbRequestHelper.getApiUrl("mediaApiBaseUrl", "PostAddFile"), - autoUpload: true, - disableImageResize: /Android(?!.*Chrome)|Opera/ - .test(window.navigator.userAgent), - previewMaxWidth: 200, - previewMaxHeight: 200, - previewCrop: true, - formData:{ - currentFolder: $routeParams.id - } - }; - - - $scope.loadChildren = function(id){ - mediaResource.getChildren(id) - .then(function(data) { - $scope.images = data.items; - }); - }; - - $scope.$on('fileuploadstop', function(event, files){ - $scope.loadChildren($scope.options.formData.currentFolder); - $scope.queue = []; - $scope.filesUploading = []; - }); - - $scope.$on('fileuploadprocessalways', function(e,data) { - var i; - $scope.$apply(function() { - $scope.filesUploading.push(data.files[data.index]); - }); - }); - - // All these sit-ups are to add dropzone area and make sure it gets removed if dragging is aborted! - $scope.$on('fileuploaddragover', function(event, files) { - if (!$scope.dragClearTimeout) { - $scope.$apply(function() { - $scope.dropping = true; - }); - } else { - $timeout.cancel($scope.dragClearTimeout); - } - $scope.dragClearTimeout = $timeout(function () { - $scope.dropping = null; - $scope.dragClearTimeout = null; - }, 300); - }); - - //init load - $scope.loadChildren($routeParams.id); - } -}); +angular.module("umbraco") +.directive("umbUploadPreview",function($parse){ + return { + link: function(scope, element, attr, ctrl) { + var fn = $parse(attr.umbUploadPreview), + file = fn(scope); + if (file.preview) { + element.append(file.preview); + } + } + }; +}) +.controller("Umbraco.PropertyEditors.FolderBrowserController", + function ($rootScope, $scope, assetsService, $routeParams, $timeout, $element, $location, umbRequestHelper, mediaResource, imageHelper) { + var dialogOptions = $scope.$parent.dialogOptions; + + $scope.creating = $routeParams.create; + + if(!$scope.creating){ + + $scope.filesUploading = []; + $scope.options = { + url: umbRequestHelper.getApiUrl("mediaApiBaseUrl", "PostAddFile"), + autoUpload: true, + disableImageResize: /Android(?!.*Chrome)|Opera/ + .test(window.navigator.userAgent), + previewMaxWidth: 200, + previewMaxHeight: 200, + previewCrop: true, + formData:{ + currentFolder: $routeParams.id + } + }; + + + $scope.loadChildren = function(id){ + mediaResource.getChildren(id) + .then(function(data) { + $scope.images = data.items; + }); + }; + + $scope.$on('fileuploadstop', function(event, files){ + $scope.loadChildren($scope.options.formData.currentFolder); + $scope.queue = []; + $scope.filesUploading = []; + }); + + $scope.$on('fileuploadprocessalways', function(e,data) { + var i; + $scope.$apply(function() { + $scope.filesUploading.push(data.files[data.index]); + }); + }); + + // All these sit-ups are to add dropzone area and make sure it gets removed if dragging is aborted! + $scope.$on('fileuploaddragover', function(event, files) { + if (!$scope.dragClearTimeout) { + $scope.$apply(function() { + $scope.dropping = true; + }); + } else { + $timeout.cancel($scope.dragClearTimeout); + } + $scope.dragClearTimeout = $timeout(function () { + $scope.dropping = null; + $scope.dragClearTimeout = null; + }, 300); + }); + + //init load + $scope.loadChildren($routeParams.id); + } +}); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/folderbrowser/folderbrowser.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/folderbrowser/folderbrowser.html index 1c9b70c077..a9c6f22aea 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/folderbrowser/folderbrowser.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/folderbrowser/folderbrowser.html @@ -1,28 +1,28 @@ -
    - - - -

    Click to upload

    - -
    - - - - - - - + + + + +

    Click to upload

    + +
    + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js index 9a1f953647..041cc7ffca 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.controller.js @@ -1,45 +1,45 @@ -angular.module("umbraco") -.controller("Umbraco.PropertyEditors.MarkdownEditorController", -//inject umbracos assetsServce and dialog service -function ($scope, assetsService, dialogService, $log, imageHelper) { - - //tell the assets service to load the markdown.editor libs from the markdown editors - //plugin folder - - if ($scope.model.value === null || $scope.model.value === "") { - $scope.model.value = $scope.model.config.defaultValue; - } - - assetsService - .load([ - "lib/markdown/markdown.converter.js", - "lib/markdown/markdown.sanitizer.js", - "lib/markdown/markdown.editor.js" - ]) - .then(function () { - - //this function will execute when all dependencies have loaded - var converter2 = new Markdown.Converter(); - var editor2 = new Markdown.Editor(converter2, "-" + $scope.model.alias); - editor2.run(); - - //subscribe to the image dialog clicks - editor2.hooks.set("insertImageDialog", function (callback) { - - - dialogService.mediaPicker({ callback: function (data) { - $(data.selection).each(function (i, item) { - var imagePropVal = imageHelper.getImagePropertyValue({ imageModel: item, scope: $scope }); - callback(imagePropVal); - }); - } - }); - - return true; // tell the editor that we'll take care of getting the image url - }); - - }); - - //load the seperat css for the editor to avoid it blocking our js loading TEMP HACK - assetsService.loadCss("lib/markdown/markdown.css"); +angular.module("umbraco") +.controller("Umbraco.PropertyEditors.MarkdownEditorController", +//inject umbracos assetsServce and dialog service +function ($scope, assetsService, dialogService, $log, imageHelper) { + + //tell the assets service to load the markdown.editor libs from the markdown editors + //plugin folder + + if ($scope.model.value === null || $scope.model.value === "") { + $scope.model.value = $scope.model.config.defaultValue; + } + + assetsService + .load([ + "lib/markdown/markdown.converter.js", + "lib/markdown/markdown.sanitizer.js", + "lib/markdown/markdown.editor.js" + ]) + .then(function () { + + //this function will execute when all dependencies have loaded + var converter2 = new Markdown.Converter(); + var editor2 = new Markdown.Editor(converter2, "-" + $scope.model.alias); + editor2.run(); + + //subscribe to the image dialog clicks + editor2.hooks.set("insertImageDialog", function (callback) { + + + dialogService.mediaPicker({ callback: function (data) { + $(data.selection).each(function (i, item) { + var imagePropVal = imageHelper.getImagePropertyValue({ imageModel: item, scope: $scope }); + callback(imagePropVal); + }); + } + }); + + return true; // tell the editor that we'll take care of getting the image url + }); + + }); + + //load the seperat css for the editor to avoid it blocking our js loading TEMP HACK + assetsService.loadCss("lib/markdown/markdown.css"); }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.html index f5aff86378..40e647b811 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/markdowneditor/markdowneditor.html @@ -1,7 +1,7 @@ -
    -
    - -
    - -
    +
    +
    + +
    + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.controller.js index 78650d0202..954ab88040 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.controller.js @@ -1,87 +1,87 @@ -//this controller simply tells the dialogs service to open a memberPicker window -//with a specified callback, this callback will receive an object with a selection on it -angular.module('umbraco') -.controller("Umbraco.PropertyEditors.MemberGroupPickerController", - - function($scope, dialogService){ - $scope.renderModel = []; - $scope.ids = []; - - - - if ($scope.model.value) { - $scope.ids = $scope.model.value.split(','); - - $($scope.ids).each(function (i, item) { - - $scope.renderModel.push({ name: item, id: item, icon: 'icon-users' }); - }); - } - - $scope.cfg = {multiPicker: true, entityType: "MemberGroup", type: "membergroup", treeAlias: "memberGroup", filter: ""}; - if($scope.model.config){ - $scope.cfg = angular.extend($scope.cfg, $scope.model.config); - } - - - - $scope.openMemberGroupPicker =function(){ - var d = dialogService.memberGroupPicker( - { - scope: $scope, - multiPicker: $scope.cfg.multiPicker, - filter: $scope.cfg.filter, - filterCssClass: "not-allowed", - callback: populate} - ); - }; - - - $scope.remove =function(index){ - $scope.renderModel.splice(index, 1); - $scope.ids.splice(index, 1); - $scope.model.value = trim($scope.ids.join(), ","); - }; - - $scope.add =function(item){ - if($scope.ids.indexOf(item) < 0){ - //item.icon = iconHelper.convertFromLegacyIcon(item.icon); - - $scope.ids.push(item); - $scope.renderModel.push({ name: item, id: item, icon: 'icon-users' }); - $scope.model.value = trim($scope.ids.join(), ","); - } - }; - - $scope.clear = function() { - $scope.model.value = ""; - $scope.renderModel = []; - $scope.ids = []; - }; - - - - $scope.$on("formSubmitting", function (ev, args) { - $scope.model.value = trim($scope.ids.join(), ","); - }); - - - - function trim(str, chr) { - var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^'+chr+'+|'+chr+'+$', 'g'); - return str.replace(rgxtrim, ''); - } - - - function populate(data){ - if(angular.isArray(data)){ - _.each(data, function (item, i) { - $scope.add(item); - }); - }else{ - $scope.clear(); - $scope.add(data); - - } - } +//this controller simply tells the dialogs service to open a memberPicker window +//with a specified callback, this callback will receive an object with a selection on it +angular.module('umbraco') +.controller("Umbraco.PropertyEditors.MemberGroupPickerController", + + function($scope, dialogService){ + $scope.renderModel = []; + $scope.ids = []; + + + + if ($scope.model.value) { + $scope.ids = $scope.model.value.split(','); + + $($scope.ids).each(function (i, item) { + + $scope.renderModel.push({ name: item, id: item, icon: 'icon-users' }); + }); + } + + $scope.cfg = {multiPicker: true, entityType: "MemberGroup", type: "membergroup", treeAlias: "memberGroup", filter: ""}; + if($scope.model.config){ + $scope.cfg = angular.extend($scope.cfg, $scope.model.config); + } + + + + $scope.openMemberGroupPicker =function(){ + var d = dialogService.memberGroupPicker( + { + scope: $scope, + multiPicker: $scope.cfg.multiPicker, + filter: $scope.cfg.filter, + filterCssClass: "not-allowed", + callback: populate} + ); + }; + + + $scope.remove =function(index){ + $scope.renderModel.splice(index, 1); + $scope.ids.splice(index, 1); + $scope.model.value = trim($scope.ids.join(), ","); + }; + + $scope.add =function(item){ + if($scope.ids.indexOf(item) < 0){ + //item.icon = iconHelper.convertFromLegacyIcon(item.icon); + + $scope.ids.push(item); + $scope.renderModel.push({ name: item, id: item, icon: 'icon-users' }); + $scope.model.value = trim($scope.ids.join(), ","); + } + }; + + $scope.clear = function() { + $scope.model.value = ""; + $scope.renderModel = []; + $scope.ids = []; + }; + + + + $scope.$on("formSubmitting", function (ev, args) { + $scope.model.value = trim($scope.ids.join(), ","); + }); + + + + function trim(str, chr) { + var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^'+chr+'+|'+chr+'+$', 'g'); + return str.replace(rgxtrim, ''); + } + + + function populate(data){ + if(angular.isArray(data)){ + _.each(data, function (item, i) { + $scope.add(item); + }); + }else{ + $scope.clear(); + $scope.add(data); + + } + } }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.html index ecb514cc61..316ad168f3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.html @@ -1,25 +1,25 @@ -
    - - - - - +
    + + + + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergroups/membergroups.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergroups/membergroups.controller.js index 7f5ba2af55..bda6424dba 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergroups/membergroups.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergroups/membergroups.controller.js @@ -1,36 +1,36 @@ -function memberGroupController($rootScope, $scope, dialogService, mediaResource, imageHelper, $log) { - - //set the available to the keys of the dictionary who's value is true - $scope.getAvailable = function () { - var available = []; - for (var n in $scope.model.value) { - if ($scope.model.value[n] === false) { - available.push(n); - } - } - return available; - }; - //set the selected to the keys of the dictionary who's value is true - $scope.getSelected = function () { - var selected = []; - for (var n in $scope.model.value) { - if ($scope.model.value[n] === true) { - selected.push(n); - } - } - return selected; - }; - - $scope.addItem = function(item) { - //keep the model up to date - $scope.model.value[item] = true; - }; - - $scope.removeItem = function (item) { - //keep the model up to date - $scope.model.value[item] = false; - }; - - -} +function memberGroupController($rootScope, $scope, dialogService, mediaResource, imageHelper, $log) { + + //set the available to the keys of the dictionary who's value is true + $scope.getAvailable = function () { + var available = []; + for (var n in $scope.model.value) { + if ($scope.model.value[n] === false) { + available.push(n); + } + } + return available; + }; + //set the selected to the keys of the dictionary who's value is true + $scope.getSelected = function () { + var selected = []; + for (var n in $scope.model.value) { + if ($scope.model.value[n] === true) { + selected.push(n); + } + } + return selected; + }; + + $scope.addItem = function(item) { + //keep the model up to date + $scope.model.value[item] = true; + }; + + $scope.removeItem = function (item) { + //keep the model up to date + $scope.model.value[item] = false; + }; + + +} angular.module('umbraco').controller("Umbraco.PropertyEditors.MemberGroupController", memberGroupController); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergroups/membergroups.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergroups/membergroups.html index 3448f1b302..bffe8682c2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergroups/membergroups.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergroups/membergroups.html @@ -1,18 +1,18 @@ -
    -
    -
    Not a member of group(s)
    - -
    -
    -
    Member of group(s)
    - -
    +
    +
    +
    Not a member of group(s)
    + +
    +
    +
    Member of group(s)
    + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js index 0e6b350dcd..afbb248e5d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js @@ -1,92 +1,92 @@ -//this controller simply tells the dialogs service to open a memberPicker window -//with a specified callback, this callback will receive an object with a selection on it -angular.module('umbraco') -.controller("Umbraco.PropertyEditors.MemberPickerController", - - function($scope, dialogService, entityResource, $log, iconHelper){ - $scope.renderModel = []; - $scope.ids = $scope.model.value.split(','); - - $scope.cfg = {multiPicker: false, entityType: "Member", type: "member", treeAlias: "member", filter: ""}; - if($scope.model.config){ - $scope.cfg = angular.extend($scope.cfg, $scope.model.config); - } - - entityResource.getByIds($scope.ids, $scope.cfg.entityType).then(function(data){ - _.each(data, function (item, i) { - item.icon = iconHelper.convertFromLegacyIcon(item.icon); - $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}); - }); - }); - - $scope.openMemberPicker =function(){ - var d = dialogService.memberPicker( - { - scope: $scope, - multiPicker: $scope.cfg.multiPicker, - filter: $scope.cfg.filter, - filterCssClass: "not-allowed", - callback: populate} - ); - }; - - - $scope.remove =function(index){ - $scope.renderModel.splice(index, 1); - $scope.ids.splice(index, 1); - $scope.model.value = trim($scope.ids.join(), ","); - }; - - $scope.add =function(item){ - if($scope.ids.indexOf(item.id) < 0){ - item.icon = iconHelper.convertFromLegacyIcon(item.icon); - - $scope.ids.push(item.id); - $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}); - $scope.model.value = trim($scope.ids.join(), ","); - } - }; - - $scope.clear = function() { - $scope.model.value = ""; - $scope.renderModel = []; - $scope.ids = []; - }; - - - $scope.sortableOptions = { - update: function(e, ui) { - var r = []; - angular.forEach($scope.renderModel, function(value, key){ - r.push(value.id); - }); - - $scope.ids = r; - $scope.model.value = trim($scope.ids.join(), ","); - } - }; - - - $scope.$on("formSubmitting", function (ev, args) { - $scope.model.value = trim($scope.ids.join(), ","); - }); - - - - function trim(str, chr) { - var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^'+chr+'+|'+chr+'+$', 'g'); - return str.replace(rgxtrim, ''); - } - - - function populate(data){ - if(angular.isArray(data)){ - _.each(data, function (item, i) { - $scope.add(item); - }); - }else{ - $scope.clear(); - $scope.add(data); - } - } +//this controller simply tells the dialogs service to open a memberPicker window +//with a specified callback, this callback will receive an object with a selection on it +angular.module('umbraco') +.controller("Umbraco.PropertyEditors.MemberPickerController", + + function($scope, dialogService, entityResource, $log, iconHelper){ + $scope.renderModel = []; + $scope.ids = $scope.model.value.split(','); + + $scope.cfg = {multiPicker: false, entityType: "Member", type: "member", treeAlias: "member", filter: ""}; + if($scope.model.config){ + $scope.cfg = angular.extend($scope.cfg, $scope.model.config); + } + + entityResource.getByIds($scope.ids, $scope.cfg.entityType).then(function(data){ + _.each(data, function (item, i) { + item.icon = iconHelper.convertFromLegacyIcon(item.icon); + $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}); + }); + }); + + $scope.openMemberPicker =function(){ + var d = dialogService.memberPicker( + { + scope: $scope, + multiPicker: $scope.cfg.multiPicker, + filter: $scope.cfg.filter, + filterCssClass: "not-allowed", + callback: populate} + ); + }; + + + $scope.remove =function(index){ + $scope.renderModel.splice(index, 1); + $scope.ids.splice(index, 1); + $scope.model.value = trim($scope.ids.join(), ","); + }; + + $scope.add =function(item){ + if($scope.ids.indexOf(item.id) < 0){ + item.icon = iconHelper.convertFromLegacyIcon(item.icon); + + $scope.ids.push(item.id); + $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}); + $scope.model.value = trim($scope.ids.join(), ","); + } + }; + + $scope.clear = function() { + $scope.model.value = ""; + $scope.renderModel = []; + $scope.ids = []; + }; + + + $scope.sortableOptions = { + update: function(e, ui) { + var r = []; + angular.forEach($scope.renderModel, function(value, key){ + r.push(value.id); + }); + + $scope.ids = r; + $scope.model.value = trim($scope.ids.join(), ","); + } + }; + + + $scope.$on("formSubmitting", function (ev, args) { + $scope.model.value = trim($scope.ids.join(), ","); + }); + + + + function trim(str, chr) { + var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^'+chr+'+|'+chr+'+$', 'g'); + return str.replace(rgxtrim, ''); + } + + + function populate(data){ + if(angular.isArray(data)){ + _.each(data, function (item, i) { + $scope.add(item); + }); + }else{ + $scope.clear(); + $scope.add(data); + } + } }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html index 1cacb21be9..473c5694e8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html @@ -1,27 +1,27 @@ -
    - - - - - +
    + + + + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js index 6aedac4297..294f9a84b6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js @@ -1,104 +1,104 @@ -angular.module("umbraco") - .controller("Umbraco.PropertyEditors.RelatedLinksController", - function ($rootScope, $scope, dialogService) { - - if (!$scope.model.value) { - $scope.model.value = []; - } - - $scope.newCaption = ''; - $scope.newLink = 'http://'; - $scope.newNewWindow = false; - $scope.newInternal = null; - $scope.newInternalName = ''; - $scope.addExternal = true; - $scope.currentEditLink = null; - $scope.hasError = false; - - $scope.internal = function ($event) { - $scope.currentEditLink = null; - var d = dialogService.contentPicker({ scope: $scope, multipicker: false, callback: select }); - $event.preventDefault(); - }; - - $scope.selectInternal = function ($event, link) { - - $scope.currentEditLink = link; - var d = dialogService.contentPicker({ scope: $scope, multipicker: false, callback: select }); - $event.preventDefault(); - }; - - $scope.edit = function (idx) { - for (var i = 0; i < $scope.model.value.length; i++) { - $scope.model.value[i].edit = false; - } - $scope.model.value[idx].edit = true; - }; - - $scope.cancelEdit = function(idx) { - $scope.model.value[idx].edit = false; - }; - - $scope.delete = function (idx) { - - $scope.model.value.splice(idx, 1); - - }; - - $scope.add = function () { - if ($scope.newCaption == "") { - $scope.hasError = true; - } else { - if ($scope.addExternal) { - var newExtLink = new function() { - this.caption = $scope.newCaption; - this.link = $scope.newLink; - this.newWindow = $scope.newNewWindow; - this.edit = false; - this.isInternal = false; - }; - $scope.model.value.push(newExtLink); - } else { - var newIntLink = new function() { - this.caption = $scope.newCaption; - this.link = $scope.newLink; - this.newWindow = $scope.newNewWindow; - this.internal = $scope.newInternal; - this.edit = false; - this.isInternal = true; - this.iternalName = $scope.newInternalName; - }; - $scope.model.value.push(newIntLink); - } - $scope.newCaption = ''; - $scope.newLink = 'http://'; - $scope.newNewWindow = false; - $scope.newInternal = null; - $scope.newInternalName = ''; - - } - }; - - $scope.switch = function ($event) { - $scope.addExternal = !$scope.addExternal; - $event.preventDefault(); - }; - - $scope.switchLinkType = function ($event,link) { - link.isInternal = !link.isInternal; - $event.preventDefault(); - }; - - function select(data) { - if ($scope.currentEditLink != null) { - $scope.currentEditLink.internal = data.id; - $scope.currentEditLink.internalName = data.name; - } else { - $scope.newInternal = data.id; - $scope.newInternalName = data.name; - } - } - - - +angular.module("umbraco") + .controller("Umbraco.PropertyEditors.RelatedLinksController", + function ($rootScope, $scope, dialogService) { + + if (!$scope.model.value) { + $scope.model.value = []; + } + + $scope.newCaption = ''; + $scope.newLink = 'http://'; + $scope.newNewWindow = false; + $scope.newInternal = null; + $scope.newInternalName = ''; + $scope.addExternal = true; + $scope.currentEditLink = null; + $scope.hasError = false; + + $scope.internal = function ($event) { + $scope.currentEditLink = null; + var d = dialogService.contentPicker({ scope: $scope, multipicker: false, callback: select }); + $event.preventDefault(); + }; + + $scope.selectInternal = function ($event, link) { + + $scope.currentEditLink = link; + var d = dialogService.contentPicker({ scope: $scope, multipicker: false, callback: select }); + $event.preventDefault(); + }; + + $scope.edit = function (idx) { + for (var i = 0; i < $scope.model.value.length; i++) { + $scope.model.value[i].edit = false; + } + $scope.model.value[idx].edit = true; + }; + + $scope.cancelEdit = function(idx) { + $scope.model.value[idx].edit = false; + }; + + $scope.delete = function (idx) { + + $scope.model.value.splice(idx, 1); + + }; + + $scope.add = function () { + if ($scope.newCaption == "") { + $scope.hasError = true; + } else { + if ($scope.addExternal) { + var newExtLink = new function() { + this.caption = $scope.newCaption; + this.link = $scope.newLink; + this.newWindow = $scope.newNewWindow; + this.edit = false; + this.isInternal = false; + }; + $scope.model.value.push(newExtLink); + } else { + var newIntLink = new function() { + this.caption = $scope.newCaption; + this.link = $scope.newLink; + this.newWindow = $scope.newNewWindow; + this.internal = $scope.newInternal; + this.edit = false; + this.isInternal = true; + this.iternalName = $scope.newInternalName; + }; + $scope.model.value.push(newIntLink); + } + $scope.newCaption = ''; + $scope.newLink = 'http://'; + $scope.newNewWindow = false; + $scope.newInternal = null; + $scope.newInternalName = ''; + + } + }; + + $scope.switch = function ($event) { + $scope.addExternal = !$scope.addExternal; + $event.preventDefault(); + }; + + $scope.switchLinkType = function ($event,link) { + link.isInternal = !link.isInternal; + $event.preventDefault(); + }; + + function select(data) { + if ($scope.currentEditLink != null) { + $scope.currentEditLink.internal = data.id; + $scope.currentEditLink.internalName = data.name; + } else { + $scope.newInternal = data.id; + $scope.newInternalName = data.name; + } + } + + + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.html index 74784ccece..47adfc3718 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.html @@ -1,80 +1,80 @@ -