diff --git a/src/Umbraco.Core/GuidUdi.cs b/src/Umbraco.Abstractions/GuidUdi.cs
similarity index 69%
rename from src/Umbraco.Core/GuidUdi.cs
rename to src/Umbraco.Abstractions/GuidUdi.cs
index d78a33a435..8acac61b32 100644
--- a/src/Umbraco.Core/GuidUdi.cs
+++ b/src/Umbraco.Abstractions/GuidUdi.cs
@@ -39,29 +39,6 @@ namespace Umbraco.Core
Guid = guid;
}
- ///
- /// Converts the string representation of an entity identifier into the equivalent GuidUdi instance.
- ///
- /// The string to convert.
- /// A GuidUdi instance that contains the value that was parsed.
- public new static GuidUdi Parse(string s)
- {
- var udi = Udi.Parse(s);
- if (udi is GuidUdi == false)
- throw new FormatException("String \"" + s + "\" is not a guid entity id.");
-
- return (GuidUdi) udi;
- }
-
- public static bool TryParse(string s, out GuidUdi udi)
- {
- Udi tmp;
- udi = null;
- if (TryParse(s, out tmp) == false) return false;
- udi = tmp as GuidUdi;
- return udi != null;
- }
-
public override bool Equals(object obj)
{
var other = obj as GuidUdi;
diff --git a/src/Umbraco.Core/NamedUdiRange.cs b/src/Umbraco.Abstractions/NamedUdiRange.cs
similarity index 100%
rename from src/Umbraco.Core/NamedUdiRange.cs
rename to src/Umbraco.Abstractions/NamedUdiRange.cs
diff --git a/src/Umbraco.Core/StringUdi.cs b/src/Umbraco.Abstractions/StringUdi.cs
similarity index 69%
rename from src/Umbraco.Core/StringUdi.cs
rename to src/Umbraco.Abstractions/StringUdi.cs
index 7b32399599..7e067ad6f9 100644
--- a/src/Umbraco.Core/StringUdi.cs
+++ b/src/Umbraco.Abstractions/StringUdi.cs
@@ -49,29 +49,6 @@ namespace Umbraco.Core
return string.Join("/", s.Split('/').Select(Uri.EscapeDataString));
}
- ///
- /// Converts the string representation of an entity identifier into the equivalent StringUdi instance.
- ///
- /// The string to convert.
- /// A StringUdi instance that contains the value that was parsed.
- public new static StringUdi Parse(string s)
- {
- var udi = Udi.Parse(s);
- if (udi is StringUdi == false)
- throw new FormatException("String \"" + s + "\" is not a string entity id.");
-
- return (StringUdi) udi;
- }
-
- public static bool TryParse(string s, out StringUdi udi)
- {
- udi = null;
- Udi tmp;
- if (TryParse(s, out tmp) == false || tmp is StringUdi == false) return false;
- udi = (StringUdi) tmp;
- return true;
- }
-
///
public override bool IsRoot
{
diff --git a/src/Umbraco.Abstractions/Udi.cs b/src/Umbraco.Abstractions/Udi.cs
new file mode 100644
index 0000000000..80e58b2f86
--- /dev/null
+++ b/src/Umbraco.Abstractions/Udi.cs
@@ -0,0 +1,171 @@
+using System;
+using System.ComponentModel;
+using System.Linq;
+
+namespace Umbraco.Core
+{
+
+ ///
+ /// Represents an entity identifier.
+ ///
+ /// An Udi can be fully qualified or "closed" eg umb://document/{guid} or "open" eg umb://document.
+ [TypeConverter(typeof(UdiTypeConverter))]
+ public abstract class Udi : IComparable
+ {
+ public Uri UriValue { get; }
+
+ ///
+ /// Initializes a new instance of the Udi class.
+ ///
+ /// The entity type part of the identifier.
+ /// The string value of the identifier.
+ protected Udi(string entityType, string stringValue)
+ {
+ EntityType = entityType;
+ UriValue = new Uri(stringValue);
+ }
+
+ ///
+ /// Initializes a new instance of the Udi class.
+ ///
+ /// The uri value of the identifier.
+ protected Udi(Uri uriValue)
+ {
+ EntityType = uriValue.Host;
+ UriValue = uriValue;
+ }
+
+
+
+ ///
+ /// Gets the entity type part of the identifier.
+ ///
+ public string EntityType { get; private set; }
+
+ public int CompareTo(Udi other)
+ {
+ return string.Compare(UriValue.ToString(), other.UriValue.ToString(), StringComparison.InvariantCultureIgnoreCase);
+ }
+
+ public override string ToString()
+ {
+ // UriValue is created in the ctor and is never null
+ // use AbsoluteUri here and not ToString else it's not encoded!
+ return UriValue.AbsoluteUri;
+ }
+
+
+
+ ///
+ /// Creates a root Udi for an entity type.
+ ///
+ /// The entity type.
+ /// The root Udi for the entity type.
+ public static Udi Create(string entityType)
+ {
+ return UdiParser.GetRootUdi(entityType);
+ }
+
+ ///
+ /// Creates a string Udi.
+ ///
+ /// The entity type.
+ /// The identifier.
+ /// The string Udi for the entity type and identifier.
+ public static Udi Create(string entityType, string id)
+ {
+ if (UdiParser.UdiTypes.TryGetValue(entityType, out var udiType) == false)
+ throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType), "entityType");
+
+ if (string.IsNullOrWhiteSpace(id))
+ throw new ArgumentException("Value cannot be null or whitespace.", "id");
+ if (udiType != UdiType.StringUdi)
+ throw new InvalidOperationException(string.Format("Entity type \"{0}\" does not have string udis.", entityType));
+
+ return new StringUdi(entityType, id);
+ }
+
+ ///
+ /// Creates a Guid Udi.
+ ///
+ /// The entity type.
+ /// The identifier.
+ /// The Guid Udi for the entity type and identifier.
+ public static Udi Create(string entityType, Guid id)
+ {
+ if (UdiParser.UdiTypes.TryGetValue(entityType, out var udiType) == false)
+ throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType), "entityType");
+
+ if (udiType != UdiType.GuidUdi)
+ throw new InvalidOperationException(string.Format("Entity type \"{0}\" does not have guid udis.", entityType));
+ if (id == default(Guid))
+ throw new ArgumentException("Cannot be an empty guid.", "id");
+
+ return new GuidUdi(entityType, id);
+ }
+
+ public static Udi Create(Uri uri)
+ {
+ // if it's a know type go fast and use ctors
+ // else fallback to parsing the string (and guess the type)
+
+ if (UdiParser.UdiTypes.TryGetValue(uri.Host, out var udiType) == false)
+ throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", uri.Host), "uri");
+
+ if (udiType == UdiType.GuidUdi)
+ return new GuidUdi(uri);
+ if (udiType == UdiType.GuidUdi)
+ return new StringUdi(uri);
+
+ throw new ArgumentException(string.Format("Uri \"{0}\" is not a valid udi.", uri));
+ }
+
+ public void EnsureType(params string[] validTypes)
+ {
+ if (validTypes.Contains(EntityType) == false)
+ throw new Exception(string.Format("Unexpected entity type \"{0}\".", EntityType));
+ }
+
+ ///
+ /// Gets a value indicating whether this Udi is a root Udi.
+ ///
+ /// A root Udi points to the "root of all things" for a given entity type, e.g. the content tree root.
+ public abstract bool IsRoot { get; }
+
+ ///
+ /// Ensures that this Udi is not a root Udi.
+ ///
+ /// This Udi.
+ /// When this Udi is a Root Udi.
+ public Udi EnsureNotRoot()
+ {
+ if (IsRoot) throw new Exception("Root Udi.");
+ return this;
+ }
+
+ public override bool Equals(object obj)
+ {
+ var other = obj as Udi;
+ return other != null && GetType() == other.GetType() && UriValue == other.UriValue;
+ }
+
+ public override int GetHashCode()
+ {
+ return UriValue.GetHashCode();
+ }
+
+ public static bool operator ==(Udi udi1, Udi udi2)
+ {
+ if (ReferenceEquals(udi1, udi2)) return true;
+ if ((object)udi1 == null || (object)udi2 == null) return false;
+ return udi1.Equals(udi2);
+ }
+
+ public static bool operator !=(Udi udi1, Udi udi2)
+ {
+ return (udi1 == udi2) == false;
+ }
+
+
+ }
+}
diff --git a/src/Umbraco.Core/UdiDefinitionAttribute.cs b/src/Umbraco.Abstractions/UdiDefinitionAttribute.cs
similarity index 100%
rename from src/Umbraco.Core/UdiDefinitionAttribute.cs
rename to src/Umbraco.Abstractions/UdiDefinitionAttribute.cs
diff --git a/src/Umbraco.Abstractions/UdiParser.cs b/src/Umbraco.Abstractions/UdiParser.cs
new file mode 100644
index 0000000000..08af2bf0cd
--- /dev/null
+++ b/src/Umbraco.Abstractions/UdiParser.cs
@@ -0,0 +1,222 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace Umbraco.Core
+{
+ public sealed class UdiParser
+ {
+ private static readonly ConcurrentDictionary RootUdis = new ConcurrentDictionary();
+ internal static ConcurrentDictionary UdiTypes { get; private set; }
+
+ static UdiParser()
+ {
+ // initialize with known (built-in) Udi types
+ // we will add scanned types later on
+ UdiTypes = new ConcurrentDictionary(GetKnownUdiTypes());
+ }
+
+ ///
+ /// Internal API for tests to resets all udi types back to only the known udi types.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void ResetUdiTypes()
+ {
+ UdiTypes = new ConcurrentDictionary(GetKnownUdiTypes());
+ }
+
+ ///
+ /// Converts the string representation of an entity identifier into the equivalent Udi instance.
+ ///
+ /// The string to convert.
+ /// An Udi instance that contains the value that was parsed.
+ public static Udi Parse(string s)
+ {
+ ParseInternal(s, false, false, out var udi);
+ return udi;
+ }
+
+ ///
+ /// Converts the string representation of an entity identifier into the equivalent Udi instance.
+ ///
+ /// The string to convert.
+ /// A value indicating whether to only deal with known types.
+ /// An Udi instance that contains the value that was parsed.
+ ///
+ /// If is true, and the string could not be parsed because
+ /// the entity type was not known, the method succeeds but sets udito an
+ /// value.
+ /// If is true, assemblies are not scanned for types,
+ /// and therefore only builtin types may be known. Unless scanning already took place.
+ ///
+ public static Udi Parse(string s, bool knownTypes)
+ {
+ ParseInternal(s, false, knownTypes, out var udi);
+ return udi;
+ }
+
+ ///
+ /// Converts the string representation of an entity identifier into the equivalent Udi instance.
+ ///
+ /// The string to convert.
+ /// An Udi instance that contains the value that was parsed.
+ /// A boolean value indicating whether the string could be parsed.
+ public static bool TryParse(string s, out Udi udi)
+ {
+ return ParseInternal(s, true, false, out udi);
+ }
+
+ ///
+ /// Converts the string representation of an entity identifier into the equivalent Udi instance.
+ ///
+ /// The string to convert.
+ /// An Udi instance that contains the value that was parsed.
+ /// A boolean value indicating whether the string could be parsed.
+ public static bool TryParse(string s, out T udi)
+ where T : Udi
+ {
+ var result = ParseInternal(s, true, false, out var parsed);
+ if (result && parsed is T)
+ {
+ udi = (T)parsed;
+ return true;
+ }
+
+ udi = null;
+ return false;
+ }
+
+ ///
+ /// Converts the string representation of an entity identifier into the equivalent Udi instance.
+ ///
+ /// The string to convert.
+ /// A value indicating whether to only deal with known types.
+ /// An Udi instance that contains the value that was parsed.
+ /// A boolean value indicating whether the string could be parsed.
+ ///
+ /// If is true, and the string could not be parsed because
+ /// the entity type was not known, the method returns false but still sets udi
+ /// to an value.
+ /// If is true, assemblies are not scanned for types,
+ /// and therefore only builtin types may be known. Unless scanning already took place.
+ ///
+ public static bool TryParse(string s, bool knownTypes, out Udi udi)
+ {
+ return ParseInternal(s, true, knownTypes, out udi);
+ }
+
+ private static bool ParseInternal(string s, bool tryParse, bool knownTypes, out Udi udi)
+ {
+ udi = null;
+ Uri uri;
+
+ if (Uri.IsWellFormedUriString(s, UriKind.Absolute) == false
+ || Uri.TryCreate(s, UriKind.Absolute, out uri) == false)
+ {
+ if (tryParse) return false;
+ throw new FormatException(string.Format("String \"{0}\" is not a valid udi.", s));
+ }
+
+ var entityType = uri.Host;
+ if (UdiTypes.TryGetValue(entityType, out var udiType) == false)
+ {
+ if (knownTypes)
+ {
+ // not knowing the type is not an error
+ // just return the unknown type udi
+ udi = UnknownTypeUdi.Instance;
+ return false;
+ }
+ if (tryParse) return false;
+ throw new FormatException(string.Format("Unknown entity type \"{0}\".", entityType));
+ }
+
+ var path = uri.AbsolutePath.TrimStart('/');
+
+ if (udiType == UdiType.GuidUdi)
+ {
+ if (path == string.Empty)
+ {
+ udi = GetRootUdi(uri.Host);
+ return true;
+ }
+ if (Guid.TryParse(path, out var guid) == false)
+ {
+ if (tryParse) return false;
+ throw new FormatException(string.Format("String \"{0}\" is not a valid udi.", s));
+ }
+ udi = new GuidUdi(uri.Host, guid);
+ return true;
+ }
+
+ if (udiType == UdiType.StringUdi)
+ {
+ udi = path == string.Empty ? GetRootUdi(uri.Host) : new StringUdi(uri.Host, Uri.UnescapeDataString(path));
+ return true;
+ }
+
+ if (tryParse) return false;
+ throw new InvalidOperationException(string.Format("Invalid udi type \"{0}\".", udiType));
+ }
+
+ internal static Udi GetRootUdi(string entityType)
+ {
+ return RootUdis.GetOrAdd(entityType, x =>
+ {
+ if (UdiTypes.TryGetValue(x, out var udiType) == false)
+ throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType));
+ return udiType == UdiType.StringUdi
+ ? (Udi)new StringUdi(entityType, string.Empty)
+ : new GuidUdi(entityType, Guid.Empty);
+ });
+ }
+
+
+
+ ///
+ /// Registers a custom entity type.
+ ///
+ ///
+ ///
+ public static void RegisterUdiType(string entityType, UdiType udiType) => UdiTypes.TryAdd(entityType, udiType);
+
+ public static Dictionary GetKnownUdiTypes() =>
+ new Dictionary
+ {
+ { Constants.UdiEntityType.Unknown, UdiType.Unknown },
+
+ { Constants.UdiEntityType.AnyGuid, UdiType.GuidUdi },
+ { Constants.UdiEntityType.Document, UdiType.GuidUdi },
+ { Constants.UdiEntityType.DocumentBlueprint, UdiType.GuidUdi },
+ { Constants.UdiEntityType.Media, UdiType.GuidUdi },
+ { Constants.UdiEntityType.Member, UdiType.GuidUdi },
+ { Constants.UdiEntityType.DictionaryItem, UdiType.GuidUdi },
+ { Constants.UdiEntityType.Macro, UdiType.GuidUdi },
+ { Constants.UdiEntityType.Template, UdiType.GuidUdi },
+ { Constants.UdiEntityType.DocumentType, UdiType.GuidUdi },
+ { Constants.UdiEntityType.DocumentTypeContainer, UdiType.GuidUdi },
+ { Constants.UdiEntityType.DocumentTypeBluePrints, UdiType.GuidUdi },
+ { Constants.UdiEntityType.MediaType, UdiType.GuidUdi },
+ { Constants.UdiEntityType.MediaTypeContainer, UdiType.GuidUdi },
+ { Constants.UdiEntityType.DataType, UdiType.GuidUdi },
+ { Constants.UdiEntityType.DataTypeContainer, UdiType.GuidUdi },
+ { Constants.UdiEntityType.MemberType, UdiType.GuidUdi },
+ { Constants.UdiEntityType.MemberGroup, UdiType.GuidUdi },
+ { Constants.UdiEntityType.RelationType, UdiType.GuidUdi },
+ { Constants.UdiEntityType.FormsForm, UdiType.GuidUdi },
+ { Constants.UdiEntityType.FormsPreValue, UdiType.GuidUdi },
+ { Constants.UdiEntityType.FormsDataSource, UdiType.GuidUdi },
+
+ { Constants.UdiEntityType.AnyString, UdiType.StringUdi },
+ { Constants.UdiEntityType.Language, UdiType.StringUdi },
+ { Constants.UdiEntityType.MacroScript, UdiType.StringUdi },
+ { Constants.UdiEntityType.MediaFile, UdiType.StringUdi },
+ { Constants.UdiEntityType.TemplateFile, UdiType.StringUdi },
+ { Constants.UdiEntityType.Script, UdiType.StringUdi },
+ { Constants.UdiEntityType.PartialView, UdiType.StringUdi },
+ { Constants.UdiEntityType.PartialViewMacro, UdiType.StringUdi },
+ { Constants.UdiEntityType.Stylesheet, UdiType.StringUdi }
+ };
+ }
+}
diff --git a/src/Umbraco.Core/UdiRange.cs b/src/Umbraco.Abstractions/UdiRange.cs
similarity index 100%
rename from src/Umbraco.Core/UdiRange.cs
rename to src/Umbraco.Abstractions/UdiRange.cs
diff --git a/src/Umbraco.Core/UdiTypeConverter.cs b/src/Umbraco.Abstractions/UdiTypeConverter.cs
similarity index 93%
rename from src/Umbraco.Core/UdiTypeConverter.cs
rename to src/Umbraco.Abstractions/UdiTypeConverter.cs
index e63aa7f786..8da1b7bb5f 100644
--- a/src/Umbraco.Core/UdiTypeConverter.cs
+++ b/src/Umbraco.Abstractions/UdiTypeConverter.cs
@@ -26,7 +26,7 @@ namespace Umbraco.Core
if (value is string)
{
Udi udi;
- if (Udi.TryParse((string)value, out udi))
+ if (UdiParser.TryParse((string)value, out udi))
{
return udi;
}
diff --git a/src/Umbraco.Abstractions/UnknownTypeUdi.cs b/src/Umbraco.Abstractions/UnknownTypeUdi.cs
new file mode 100644
index 0000000000..ac42205cbc
--- /dev/null
+++ b/src/Umbraco.Abstractions/UnknownTypeUdi.cs
@@ -0,0 +1,16 @@
+namespace Umbraco.Core
+{
+ internal class UnknownTypeUdi : Udi
+ {
+ private UnknownTypeUdi()
+ : base("unknown", "umb://unknown/")
+ { }
+
+ public static readonly UnknownTypeUdi Instance = new UnknownTypeUdi();
+
+ public override bool IsRoot
+ {
+ get { return false; }
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs
index 1956876402..343557e3f5 100644
--- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs
+++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs
@@ -64,7 +64,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
GuidUdi udi = null;
if (relatedLink.IsInternal)
{
- var linkIsUdi = GuidUdi.TryParse(relatedLink.Link, out udi);
+ var linkIsUdi = UdiParser.TryParse(relatedLink.Link, out udi);
if (linkIsUdi == false)
{
// oh no.. probably an integer, yikes!
diff --git a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs
index 5449318e7a..a9bcbc2b65 100644
--- a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs
+++ b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs
@@ -1,6 +1,4 @@
-using System;
-using System.ComponentModel;
-using Umbraco.Core.CodeAnnotations;
+using Umbraco.Core.CodeAnnotations;
namespace Umbraco.Core.Models
{
diff --git a/src/Umbraco.Core/Serialization/KnownTypeUdiJsonConverter.cs b/src/Umbraco.Core/Serialization/KnownTypeUdiJsonConverter.cs
index e6473e7f8e..79ff90c734 100644
--- a/src/Umbraco.Core/Serialization/KnownTypeUdiJsonConverter.cs
+++ b/src/Umbraco.Core/Serialization/KnownTypeUdiJsonConverter.cs
@@ -20,7 +20,7 @@ namespace Umbraco.Core.Serialization
{
var jo = JToken.ReadFrom(reader);
var val = jo.ToObject();
- return val == null ? null : Udi.Parse(val, true);
+ return val == null ? null : UdiParser.Parse(val, true);
}
}
}
diff --git a/src/Umbraco.Core/Serialization/UdiJsonConverter.cs b/src/Umbraco.Core/Serialization/UdiJsonConverter.cs
index 134faf3d1d..eac5dd8c26 100644
--- a/src/Umbraco.Core/Serialization/UdiJsonConverter.cs
+++ b/src/Umbraco.Core/Serialization/UdiJsonConverter.cs
@@ -20,7 +20,7 @@ namespace Umbraco.Core.Serialization
{
var jo = JToken.ReadFrom(reader);
var val = jo.ToObject();
- return val == null ? null : Udi.Parse(val);
+ return val == null ? null : UdiParser.Parse(val);
}
}
}
diff --git a/src/Umbraco.Core/Udi.cs b/src/Umbraco.Core/Udi.cs
deleted file mode 100644
index 444cbda6f5..0000000000
--- a/src/Umbraco.Core/Udi.cs
+++ /dev/null
@@ -1,385 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using Umbraco.Core.Deploy;
-using Umbraco.Core.Composing;
-
-namespace Umbraco.Core
-{
- ///
- /// Represents an entity identifier.
- ///
- /// An Udi can be fully qualified or "closed" eg umb://document/{guid} or "open" eg umb://document.
- [TypeConverter(typeof(UdiTypeConverter))]
- public abstract class Udi : IComparable
- {
- // notes - see U4-10409
- // if this class is used during application pre-start it cannot scans the assemblies,
- // this is addressed by lazily-scanning, with the following caveats:
- // - parsing a root udi still requires a scan and therefore still breaks
- // - parsing an invalid udi ("umb://should-be-guid/") corrupts KnowUdiTypes
-
- private static volatile bool _scanned;
- private static readonly object ScanLocker = new object();
- private static ConcurrentDictionary _udiTypes;
- private static readonly ConcurrentDictionary RootUdis = new ConcurrentDictionary();
- internal readonly Uri UriValue; // internal for UdiRange
-
- ///
- /// Initializes a new instance of the Udi class.
- ///
- /// The entity type part of the identifier.
- /// The string value of the identifier.
- protected Udi(string entityType, string stringValue)
- {
- EntityType = entityType;
- UriValue = new Uri(stringValue);
- }
-
- ///
- /// Initializes a new instance of the Udi class.
- ///
- /// The uri value of the identifier.
- protected Udi(Uri uriValue)
- {
- EntityType = uriValue.Host;
- UriValue = uriValue;
- }
-
- static Udi()
- {
- // initialize with known (built-in) Udi types
- // we will add scanned types later on
- _udiTypes = new ConcurrentDictionary(UdiEntityTypeHelper.GetTypes());
- }
-
- // for tests, totally unsafe
- internal static void ResetUdiTypes()
- {
- _udiTypes = new ConcurrentDictionary(UdiEntityTypeHelper.GetTypes());
- _scanned = false;
- }
-
- ///
- /// Gets the entity type part of the identifier.
- ///
- public string EntityType { get; private set; }
-
- public int CompareTo(Udi other)
- {
- return string.Compare(UriValue.ToString(), other.UriValue.ToString(), StringComparison.InvariantCultureIgnoreCase);
- }
-
- public override string ToString()
- {
- // UriValue is created in the ctor and is never null
- // use AbsoluteUri here and not ToString else it's not encoded!
- return UriValue.AbsoluteUri;
- }
-
- ///
- /// Converts the string representation of an entity identifier into the equivalent Udi instance.
- ///
- /// The string to convert.
- /// An Udi instance that contains the value that was parsed.
- public static Udi Parse(string s)
- {
- Udi udi;
- ParseInternal(s, false, false, out udi);
- return udi;
- }
-
- ///
- /// Converts the string representation of an entity identifier into the equivalent Udi instance.
- ///
- /// The string to convert.
- /// A value indicating whether to only deal with known types.
- /// An Udi instance that contains the value that was parsed.
- ///
- /// If is true, and the string could not be parsed because
- /// the entity type was not known, the method succeeds but sets udito an
- /// value.
- /// If is true, assemblies are not scanned for types,
- /// and therefore only builtin types may be known. Unless scanning already took place.
- ///
- public static Udi Parse(string s, bool knownTypes)
- {
- Udi udi;
- ParseInternal(s, false, knownTypes, out udi);
- return udi;
- }
-
- ///
- /// Converts the string representation of an entity identifier into the equivalent Udi instance.
- ///
- /// The string to convert.
- /// An Udi instance that contains the value that was parsed.
- /// A boolean value indicating whether the string could be parsed.
- public static bool TryParse(string s, out Udi udi)
- {
- return ParseInternal(s, true, false, out udi);
- }
-
- ///
- /// Converts the string representation of an entity identifier into the equivalent Udi instance.
- ///
- /// The string to convert.
- /// A value indicating whether to only deal with known types.
- /// An Udi instance that contains the value that was parsed.
- /// A boolean value indicating whether the string could be parsed.
- ///
- /// If is true, and the string could not be parsed because
- /// the entity type was not known, the method returns false but still sets udi
- /// to an value.
- /// If is true, assemblies are not scanned for types,
- /// and therefore only builtin types may be known. Unless scanning already took place.
- ///
- public static bool TryParse(string s, bool knownTypes, out Udi udi)
- {
- return ParseInternal(s, true, knownTypes, out udi);
- }
-
- private static bool ParseInternal(string s, bool tryParse, bool knownTypes, out Udi udi)
- {
- if (knownTypes == false)
- EnsureScanForUdiTypes();
-
- udi = null;
- Uri uri;
-
- if (Uri.IsWellFormedUriString(s, UriKind.Absolute) == false
- || Uri.TryCreate(s, UriKind.Absolute, out uri) == false)
- {
- if (tryParse) return false;
- throw new FormatException(string.Format("String \"{0}\" is not a valid udi.", s));
- }
-
- var entityType = uri.Host;
- UdiType udiType;
- if (_udiTypes.TryGetValue(entityType, out udiType) == false)
- {
- if (knownTypes)
- {
- // not knowing the type is not an error
- // just return the unknown type udi
- udi = UnknownTypeUdi.Instance;
- return false;
- }
- if (tryParse) return false;
- throw new FormatException(string.Format("Unknown entity type \"{0}\".", entityType));
- }
-
- var path = uri.AbsolutePath.TrimStart('/');
-
- if (udiType == UdiType.GuidUdi)
- {
- if (path == string.Empty)
- {
- udi = GetRootUdi(uri.Host);
- return true;
- }
- Guid guid;
- if (Guid.TryParse(path, out guid) == false)
- {
- if (tryParse) return false;
- throw new FormatException(string.Format("String \"{0}\" is not a valid udi.", s));
- }
- udi = new GuidUdi(uri.Host, guid);
- return true;
- }
-
- if (udiType == UdiType.StringUdi)
- {
- udi = path == string.Empty ? GetRootUdi(uri.Host) : new StringUdi(uri.Host, Uri.UnescapeDataString(path));
- return true;
- }
-
- if (tryParse) return false;
- throw new InvalidOperationException(string.Format("Invalid udi type \"{0}\".", udiType));
- }
-
- private static Udi GetRootUdi(string entityType)
- {
- EnsureScanForUdiTypes();
-
- return RootUdis.GetOrAdd(entityType, x =>
- {
- UdiType udiType;
- if (_udiTypes.TryGetValue(x, out udiType) == false)
- throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType));
- return udiType == UdiType.StringUdi
- ? (Udi)new StringUdi(entityType, string.Empty)
- : new GuidUdi(entityType, Guid.Empty);
- });
- }
-
- ///
- /// When required scan assemblies for known UDI types based on instances
- ///
- ///
- /// This is only required when needing to resolve root udis
- ///
- private static void EnsureScanForUdiTypes()
- {
- if (_scanned) return;
-
- lock (ScanLocker)
- {
- // Scan for unknown UDI types
- // there is no way we can get the "registered" service connectors, as registration
- // happens in Deploy, not in Core, and the Udi class belongs to Core - therefore, we
- // just pick every service connectors - just making sure that not two of them
- // would register the same entity type, with different udi types (would not make
- // much sense anyways).
- var connectors = Current.TypeLoader.GetTypes();
- var result = new Dictionary();
- foreach (var connector in connectors)
- {
- var attrs = connector.GetCustomAttributes(false);
- foreach (var attr in attrs)
- {
- UdiType udiType;
- if (result.TryGetValue(attr.EntityType, out udiType) && udiType != attr.UdiType)
- throw new Exception(string.Format("Entity type \"{0}\" is declared by more than one IServiceConnector, with different UdiTypes.", attr.EntityType));
- result[attr.EntityType] = attr.UdiType;
- }
- }
-
- // merge these into the known list
- foreach (var item in result)
- _udiTypes.TryAdd(item.Key, item.Value);
-
- _scanned = true;
- }
- }
-
- ///
- /// Creates a root Udi for an entity type.
- ///
- /// The entity type.
- /// The root Udi for the entity type.
- public static Udi Create(string entityType)
- {
- return GetRootUdi(entityType);
- }
-
- ///
- /// Creates a string Udi.
- ///
- /// The entity type.
- /// The identifier.
- /// The string Udi for the entity type and identifier.
- public static Udi Create(string entityType, string id)
- {
- UdiType udiType;
- if (_udiTypes.TryGetValue(entityType, out udiType) == false)
- throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType), "entityType");
-
- if (string.IsNullOrWhiteSpace(id))
- throw new ArgumentException("Value cannot be null or whitespace.", "id");
- if (udiType != UdiType.StringUdi)
- throw new InvalidOperationException(string.Format("Entity type \"{0}\" does not have string udis.", entityType));
-
- return new StringUdi(entityType, id);
- }
-
- ///
- /// Creates a Guid Udi.
- ///
- /// The entity type.
- /// The identifier.
- /// The Guid Udi for the entity type and identifier.
- public static Udi Create(string entityType, Guid id)
- {
- UdiType udiType;
- if (_udiTypes.TryGetValue(entityType, out udiType) == false)
- throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType), "entityType");
-
- if (udiType != UdiType.GuidUdi)
- throw new InvalidOperationException(string.Format("Entity type \"{0}\" does not have guid udis.", entityType));
- if (id == default(Guid))
- throw new ArgumentException("Cannot be an empty guid.", "id");
-
- return new GuidUdi(entityType, id);
- }
-
- internal static Udi Create(Uri uri)
- {
- // if it's a know type go fast and use ctors
- // else fallback to parsing the string (and guess the type)
-
- UdiType udiType;
- if (_udiTypes.TryGetValue(uri.Host, out udiType) == false)
- throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", uri.Host), "uri");
-
- if (udiType == UdiType.GuidUdi)
- return new GuidUdi(uri);
- if (udiType == UdiType.GuidUdi)
- return new StringUdi(uri);
-
- throw new ArgumentException(string.Format("Uri \"{0}\" is not a valid udi.", uri));
- }
-
- public void EnsureType(params string[] validTypes)
- {
- if (validTypes.Contains(EntityType) == false)
- throw new Exception(string.Format("Unexpected entity type \"{0}\".", EntityType));
- }
-
- ///
- /// Gets a value indicating whether this Udi is a root Udi.
- ///
- /// A root Udi points to the "root of all things" for a given entity type, e.g. the content tree root.
- public abstract bool IsRoot { get; }
-
- ///
- /// Ensures that this Udi is not a root Udi.
- ///
- /// This Udi.
- /// When this Udi is a Root Udi.
- public Udi EnsureNotRoot()
- {
- if (IsRoot) throw new Exception("Root Udi.");
- return this;
- }
-
- public override bool Equals(object obj)
- {
- var other = obj as Udi;
- return other != null && GetType() == other.GetType() && UriValue == other.UriValue;
- }
-
- public override int GetHashCode()
- {
- return UriValue.GetHashCode();
- }
-
- public static bool operator ==(Udi udi1, Udi udi2)
- {
- if (ReferenceEquals(udi1, udi2)) return true;
- if ((object)udi1 == null || (object)udi2 == null) return false;
- return udi1.Equals(udi2);
- }
-
- public static bool operator !=(Udi udi1, Udi udi2)
- {
- return (udi1 == udi2) == false;
- }
-
- private class UnknownTypeUdi : Udi
- {
- private UnknownTypeUdi()
- : base("unknown", "umb://unknown/")
- { }
-
- public static readonly UnknownTypeUdi Instance = new UnknownTypeUdi();
-
- public override bool IsRoot
- {
- get { return false; }
- }
- }
- }
-}
diff --git a/src/Umbraco.Core/UdiEntityTypeHelper.cs b/src/Umbraco.Core/UdiEntityTypeHelper.cs
index 6504ddc674..12e934b9ec 100644
--- a/src/Umbraco.Core/UdiEntityTypeHelper.cs
+++ b/src/Umbraco.Core/UdiEntityTypeHelper.cs
@@ -6,43 +6,7 @@ namespace Umbraco.Core
{
public static class UdiEntityTypeHelper
{
- internal static Dictionary GetTypes() =>
- new Dictionary
- {
- { Constants.UdiEntityType.Unknown, UdiType.Unknown },
-
- { Constants.UdiEntityType.AnyGuid, UdiType.GuidUdi },
- { Constants.UdiEntityType.Document, UdiType.GuidUdi },
- { Constants.UdiEntityType.DocumentBlueprint, UdiType.GuidUdi },
- { Constants.UdiEntityType.Media, UdiType.GuidUdi },
- { Constants.UdiEntityType.Member, UdiType.GuidUdi },
- { Constants.UdiEntityType.DictionaryItem, UdiType.GuidUdi },
- { Constants.UdiEntityType.Macro, UdiType.GuidUdi },
- { Constants.UdiEntityType.Template, UdiType.GuidUdi },
- { Constants.UdiEntityType.DocumentType, UdiType.GuidUdi },
- { Constants.UdiEntityType.DocumentTypeContainer, UdiType.GuidUdi },
- { Constants.UdiEntityType.DocumentTypeBluePrints, UdiType.GuidUdi },
- { Constants.UdiEntityType.MediaType, UdiType.GuidUdi },
- { Constants.UdiEntityType.MediaTypeContainer, UdiType.GuidUdi },
- { Constants.UdiEntityType.DataType, UdiType.GuidUdi },
- { Constants.UdiEntityType.DataTypeContainer, UdiType.GuidUdi },
- { Constants.UdiEntityType.MemberType, UdiType.GuidUdi },
- { Constants.UdiEntityType.MemberGroup, UdiType.GuidUdi },
- { Constants.UdiEntityType.RelationType, UdiType.GuidUdi },
- { Constants.UdiEntityType.FormsForm, UdiType.GuidUdi },
- { Constants.UdiEntityType.FormsPreValue, UdiType.GuidUdi },
- { Constants.UdiEntityType.FormsDataSource, UdiType.GuidUdi },
-
- { Constants.UdiEntityType.AnyString, UdiType.StringUdi },
- { Constants.UdiEntityType.Language, UdiType.StringUdi },
- { Constants.UdiEntityType.MacroScript, UdiType.StringUdi },
- { Constants.UdiEntityType.MediaFile, UdiType.StringUdi },
- { Constants.UdiEntityType.TemplateFile, UdiType.StringUdi },
- { Constants.UdiEntityType.Script, UdiType.StringUdi },
- { Constants.UdiEntityType.PartialView, UdiType.StringUdi },
- { Constants.UdiEntityType.PartialViewMacro, UdiType.StringUdi },
- { Constants.UdiEntityType.Stylesheet, UdiType.StringUdi }
- };
+
public static string FromUmbracoObjectType(UmbracoObjectTypes umbracoObjectType)
{
diff --git a/src/Umbraco.Core/UdiParserServiceConnectors.cs b/src/Umbraco.Core/UdiParserServiceConnectors.cs
new file mode 100644
index 0000000000..6137b0bde9
--- /dev/null
+++ b/src/Umbraco.Core/UdiParserServiceConnectors.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Umbraco.Core.Deploy;
+using Umbraco.Core.Composing;
+
+namespace Umbraco.Core
+{
+ public static class UdiParserServiceConnectors
+ {
+ // notes - see U4-10409
+ // if this class is used during application pre-start it cannot scans the assemblies,
+ // this is addressed by lazily-scanning, with the following caveats:
+ // - parsing a root udi still requires a scan and therefore still breaks
+ // - parsing an invalid udi ("umb://should-be-guid/") corrupts KnowUdiTypes
+
+ private static volatile bool _scanned;
+ private static readonly object ScanLocker = new object();
+
+ ///
+ /// Scan for deploy in assemblies for known UDI types.
+ ///
+ ///
+ public static void ScanDeployServiceConnectorsForUdiTypes(TypeLoader typeLoader)
+ {
+ if (typeLoader is null)
+ throw new ArgumentNullException(nameof(typeLoader));
+
+ if (_scanned) return;
+
+ lock (ScanLocker)
+ {
+ // Scan for unknown UDI types
+ // there is no way we can get the "registered" service connectors, as registration
+ // happens in Deploy, not in Core, and the Udi class belongs to Core - therefore, we
+ // just pick every service connectors - just making sure that not two of them
+ // would register the same entity type, with different udi types (would not make
+ // much sense anyways)
+ var connectors = typeLoader.GetTypes();
+ var result = new Dictionary();
+ foreach (var connector in connectors)
+ {
+ var attrs = connector.GetCustomAttributes(false);
+ foreach (var attr in attrs)
+ {
+ if (result.TryGetValue(attr.EntityType, out var udiType) && udiType != attr.UdiType)
+ throw new Exception(string.Format("Entity type \"{0}\" is declared by more than one IServiceConnector, with different UdiTypes.", attr.EntityType));
+ result[attr.EntityType] = attr.UdiType;
+ }
+ }
+
+ // merge these into the known list
+ foreach (var item in result)
+ UdiParser.RegisterUdiType(item.Key, item.Value);
+
+ _scanned = true;
+ }
+ }
+
+ ///
+ /// Registers a single to add it's UDI type.
+ ///
+ ///
+ public static void RegisterServiceConnector()
+ where T: IServiceConnector
+ {
+ var result = new Dictionary();
+ var connector = typeof(T);
+ var attrs = connector.GetCustomAttributes(false);
+ foreach (var attr in attrs)
+ {
+ if (result.TryGetValue(attr.EntityType, out var udiType) && udiType != attr.UdiType)
+ throw new Exception(string.Format("Entity type \"{0}\" is declared by more than one IServiceConnector, with different UdiTypes.", attr.EntityType));
+ result[attr.EntityType] = attr.UdiType;
+ }
+
+ // merge these into the known list
+ foreach (var item in result)
+ UdiParser.RegisterUdiType(item.Key, item.Value);
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 5ca4efc67d..ab5198f677 100755
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -527,7 +527,6 @@
-
@@ -658,7 +657,6 @@
-
@@ -1095,17 +1093,13 @@
-
-
-
-
-
+
diff --git a/src/Umbraco.Tests/CoreThings/UdiTests.cs b/src/Umbraco.Tests/CoreThings/UdiTests.cs
index dc2265eac2..df5d5363e5 100644
--- a/src/Umbraco.Tests/CoreThings/UdiTests.cs
+++ b/src/Umbraco.Tests/CoreThings/UdiTests.cs
@@ -24,21 +24,7 @@ namespace Umbraco.Tests.CoreThings
[SetUp]
public void SetUp()
{
- // FIXME: bad in a unit test - but Udi has a static ctor that wants it?!
- var container = new Mock();
- var ioHelper = IOHelper.Default;
- var typeFinder = new TypeFinder(Mock.Of());
- container.Setup(x => x.GetInstance(typeof(TypeLoader))).Returns(
- new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), new ProfilingLogger(Mock.Of(), Mock.Of())));
- Current.Factory = container.Object;
-
- Udi.ResetUdiTypes();
- }
-
- [TearDown]
- public void TearDown()
- {
- Current.Reset();
+ UdiParser.ResetUdiTypes();
}
[Test]
@@ -53,7 +39,7 @@ namespace Umbraco.Tests.CoreThings
[Test]
public void StringUdiParseTest()
{
- var udi = Udi.Parse("umb://" + Constants.UdiEntityType.AnyString + "/test-id");
+ var udi = UdiParser.Parse("umb://" + Constants.UdiEntityType.AnyString + "/test-id");
Assert.AreEqual(Constants.UdiEntityType.AnyString, udi.EntityType);
Assert.IsInstanceOf(udi);
var stringEntityId = udi as StringUdi;
@@ -61,7 +47,7 @@ namespace Umbraco.Tests.CoreThings
Assert.AreEqual("test-id", stringEntityId.Id);
Assert.AreEqual("umb://" + Constants.UdiEntityType.AnyString + "/test-id", udi.ToString());
- udi = Udi.Parse("umb://" + Constants.UdiEntityType.AnyString + "/DA845952BE474EE9BD6F6194272AC750");
+ udi = UdiParser.Parse("umb://" + Constants.UdiEntityType.AnyString + "/DA845952BE474EE9BD6F6194272AC750");
Assert.IsInstanceOf(udi);
}
@@ -78,7 +64,7 @@ namespace Umbraco.Tests.CoreThings
Assert.AreEqual("%2Fthis%20is%20a%20test", Uri.EscapeDataString("/this is a test"));
Assert.AreEqual("/this%20is%20a%20test", Uri.EscapeUriString("/this is a test"));
- var udi = Udi.Parse("umb://" + Constants.UdiEntityType.AnyString + "/this%20is%20a%20test");
+ var udi = UdiParser.Parse("umb://" + Constants.UdiEntityType.AnyString + "/this%20is%20a%20test");
Assert.AreEqual(Constants.UdiEntityType.AnyString, udi.EntityType);
Assert.IsInstanceOf(udi);
var stringEntityId = udi as StringUdi;
@@ -112,7 +98,7 @@ namespace Umbraco.Tests.CoreThings
// with the proper fix in StringUdi this should work:
var udi1 = new StringUdi("partial-view-macro", "path/to/View[1].cshtml");
Assert.AreEqual("umb://partial-view-macro/path/to/View%5B1%5D.cshtml", udi1.ToString());
- var udi2 = Udi.Parse("umb://partial-view-macro/path/to/View%5B1%5D.cshtml");
+ var udi2 = UdiParser.Parse("umb://partial-view-macro/path/to/View%5B1%5D.cshtml");
Assert.AreEqual("path/to/View[1].cshtml", ((StringUdi) udi2).Id);
}
@@ -131,7 +117,7 @@ namespace Umbraco.Tests.CoreThings
{
var guid = Guid.NewGuid();
var s = "umb://" + Constants.UdiEntityType.AnyGuid + "/" + guid.ToString("N");
- var udi = Udi.Parse(s);
+ var udi = UdiParser.Parse(s);
Assert.AreEqual(Constants.UdiEntityType.AnyGuid, udi.EntityType);
Assert.IsInstanceOf(udi);
var gudi = udi as GuidUdi;
@@ -198,15 +184,15 @@ namespace Umbraco.Tests.CoreThings
Assert.IsTrue(guidUdi.IsRoot);
Assert.AreEqual("umb://any-guid/00000000000000000000000000000000", guidUdi.ToString());
- var udi = Udi.Parse("umb://any-string/");
+ var udi = UdiParser.Parse("umb://any-string/");
Assert.IsTrue(udi.IsRoot);
Assert.IsInstanceOf(udi);
- udi = Udi.Parse("umb://any-guid/00000000000000000000000000000000");
+ udi = UdiParser.Parse("umb://any-guid/00000000000000000000000000000000");
Assert.IsTrue(udi.IsRoot);
Assert.IsInstanceOf(udi);
- udi = Udi.Parse("umb://any-guid/");
+ udi = UdiParser.Parse("umb://any-guid/");
Assert.IsTrue(udi.IsRoot);
Assert.IsInstanceOf(udi);
}
@@ -217,13 +203,13 @@ namespace Umbraco.Tests.CoreThings
// can parse open string udi
var stringUdiString = "umb://" + Constants.UdiEntityType.AnyString;
Udi stringUdi;
- Assert.IsTrue(Udi.TryParse(stringUdiString, out stringUdi));
+ Assert.IsTrue(UdiParser.TryParse(stringUdiString, out stringUdi));
Assert.AreEqual(string.Empty, ((StringUdi)stringUdi).Id);
// can parse open guid udi
var guidUdiString = "umb://" + Constants.UdiEntityType.AnyGuid;
Udi guidUdi;
- Assert.IsTrue(Udi.TryParse(guidUdiString, out guidUdi));
+ Assert.IsTrue(UdiParser.TryParse(guidUdiString, out guidUdi));
Assert.AreEqual(Guid.Empty, ((GuidUdi)guidUdi).Guid);
// can create a range
@@ -264,7 +250,7 @@ namespace Umbraco.Tests.CoreThings
[Test]
public void ValidateUdiEntityType()
{
- var types = UdiEntityTypeHelper.GetTypes();
+ var types = UdiParser.GetKnownUdiTypes();
foreach (var fi in typeof(Constants.UdiEntityType).GetFields(BindingFlags.Public | BindingFlags.Static))
{
@@ -294,33 +280,34 @@ namespace Umbraco.Tests.CoreThings
// cannot parse an unknown type, udi is null
// this will scan
- Assert.IsFalse(Udi.TryParse("umb://whatever/1234", out udi));
+ Assert.IsFalse(UdiParser.TryParse("umb://whatever/1234", out udi));
Assert.IsNull(udi);
- Udi.ResetUdiTypes();
-
+ UdiParser.ResetUdiTypes();
+
// unless we want to know
- Assert.IsFalse(Udi.TryParse("umb://whatever/1234", true, out udi));
+ Assert.IsFalse(UdiParser.TryParse("umb://whatever/1234", true, out udi));
Assert.AreEqual(Constants.UdiEntityType.Unknown, udi.EntityType);
- Assert.AreEqual("Umbraco.Core.Udi+UnknownTypeUdi", udi.GetType().FullName);
-
- Udi.ResetUdiTypes();
+ Assert.AreEqual("Umbraco.Core.UnknownTypeUdi", udi.GetType().FullName);
+ UdiParser.ResetUdiTypes();
+
// not known
- Assert.IsFalse(Udi.TryParse("umb://foo/A87F65C8D6B94E868F6949BA92C93045", true, out udi));
+ Assert.IsFalse(UdiParser.TryParse("umb://foo/A87F65C8D6B94E868F6949BA92C93045", true, out udi));
Assert.AreEqual(Constants.UdiEntityType.Unknown, udi.EntityType);
- Assert.AreEqual("Umbraco.Core.Udi+UnknownTypeUdi", udi.GetType().FullName);
+ Assert.AreEqual("Umbraco.Core.UnknownTypeUdi", udi.GetType().FullName);
- // scanned
- Assert.IsTrue(Udi.TryParse("umb://foo/A87F65C8D6B94E868F6949BA92C93045", out udi));
+ // scanned
+ UdiParserServiceConnectors.RegisterServiceConnector(); // this is the equivalent of scanning but we'll just manually register this one
+ Assert.IsTrue(UdiParser.TryParse("umb://foo/A87F65C8D6B94E868F6949BA92C93045", out udi));
Assert.IsInstanceOf(udi);
// known
- Assert.IsTrue(Udi.TryParse("umb://foo/A87F65C8D6B94E868F6949BA92C93045", true, out udi));
+ Assert.IsTrue(UdiParser.TryParse("umb://foo/A87F65C8D6B94E868F6949BA92C93045", true, out udi));
Assert.IsInstanceOf(udi);
// can get method for Deploy compatibility
- var method = typeof(Udi).GetMethod("Parse", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(string), typeof(bool) }, null);
+ var method = typeof(UdiParser).GetMethod("Parse", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(string), typeof(bool) }, null);
Assert.IsNotNull(method);
}
diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs
index e3678a1db5..b13532b824 100644
--- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs
+++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs
@@ -1,14 +1,10 @@
using System;
-using System.IO;
using System.Linq;
using System.Web;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
-using Umbraco.Core.Cache;
-using Umbraco.Core.Composing;
using Umbraco.Core.Configuration.UmbracoSettings;
-using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Services;
@@ -17,10 +13,7 @@ using Umbraco.Tests.Testing.Objects.Accessors;
using Umbraco.Web;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Routing;
-using Umbraco.Web.Security;
using Umbraco.Web.Templates;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.IO;
namespace Umbraco.Tests.Web
{
@@ -30,37 +23,9 @@ namespace Umbraco.Tests.Web
[SetUp]
public void SetUp()
{
- Current.Reset();
-
- // FIXME: now UrlProvider depends on EntityService for GetUrl(guid) - this is bad
- // should not depend on more than IdkMap maybe - fix this!
- var entityService = new Mock();
- entityService.Setup(x => x.GetId(It.IsAny(), It.IsAny())).Returns(Attempt.Fail());
- var serviceContext = ServiceContext.CreatePartial(entityService: entityService.Object);
-
- // FIXME: bad in a unit test - but Udi has a static ctor that wants it?!
- var factory = new Mock();
- var typeFinder = new TypeFinder(Mock.Of());
- var ioHelper = IOHelper.Default;
- factory.Setup(x => x.GetInstance(typeof(TypeLoader))).Returns(
- new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), new ProfilingLogger(Mock.Of(), Mock.Of())));
- factory.Setup(x => x.GetInstance(typeof (ServiceContext))).Returns(serviceContext);
-
- var settings = SettingsForTests.GetDefaultUmbracoSettings();
- factory.Setup(x => x.GetInstance(typeof(IUmbracoSettingsSection))).Returns(settings);
-
- Current.Factory = factory.Object;
-
- Umbraco.Web.Composing.Current.UmbracoContextAccessor = new TestUmbracoContextAccessor();
-
- Udi.ResetUdiTypes();
+ UdiParser.ResetUdiTypes();
}
- [TearDown]
- public void TearDown()
- {
- Current.Reset();
- }
[TestCase("", "")]
[TestCase("hello href=\"{localLink:1234}\" world ", "hello href=\"/my-test-url\" world ")]
@@ -71,17 +36,7 @@ namespace Umbraco.Tests.Web
[TestCase("hello href=\"{localLink:umb^://document/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"{localLink:umb^://document/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ")]
[TestCase("hello href=\"{localLink:umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"#\" world ")]
public void ParseLocalLinks(string input, string result)
- {
- var serviceCtxMock = new TestObjects(null).GetServiceContextMock();
-
- //setup a mock entity service from the service context to return an integer for a GUID
- var entityService = Mock.Get(serviceCtxMock.EntityService);
- //entityService.Setup(x => x.GetId(It.IsAny(), It.IsAny()))
- // .Returns((Guid id, UmbracoObjectTypes objType) =>
- // {
- // return Attempt.Succeed(1234);
- // });
-
+ {
//setup a mock url provider which we'll use for testing
var testUrlProvider = new Mock();
testUrlProvider
@@ -106,8 +61,9 @@ namespace Umbraco.Tests.Web
var mediaCache = Mock.Of();
Mock.Get(mediaCache).Setup(x => x.GetById(It.IsAny())).Returns(media);
+ var umbracoContextAccessor = new TestUmbracoContextAccessor();
var umbracoContextFactory = new UmbracoContextFactory(
- Umbraco.Web.Composing.Current.UmbracoContextAccessor,
+ umbracoContextAccessor,
snapshotService,
new TestVariationContextAccessor(),
new TestDefaultCultureAccessor(),
diff --git a/src/Umbraco.Tests/Web/UmbracoHelperTests.cs b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs
index cdf17e12e9..425f24be41 100644
--- a/src/Umbraco.Tests/Web/UmbracoHelperTests.cs
+++ b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs
@@ -176,7 +176,7 @@ namespace Umbraco.Tests.Web
public static void Converting_Boxed_Udi_To_A_Udi_Returns_Original_Udi_Value()
{
// Arrange
- Udi.ResetUdiTypes();
+ UdiParser.ResetUdiTypes();
Udi sample = new GuidUdi(Constants.UdiEntityType.AnyGuid, Guid.NewGuid());
// Act
@@ -198,7 +198,7 @@ namespace Umbraco.Tests.Web
{
// Arrange
SetUpDependencyContainer();
- Udi.ResetUdiTypes();
+ UdiParser.ResetUdiTypes();
Udi sample = new GuidUdi(Constants.UdiEntityType.AnyGuid, Guid.NewGuid());
// Act
@@ -220,7 +220,7 @@ namespace Umbraco.Tests.Web
{
// Arrange
SetUpDependencyContainer();
- Udi.ResetUdiTypes();
+ UdiParser.ResetUdiTypes();
const string sample = "Hello";
// Act
@@ -241,7 +241,7 @@ namespace Umbraco.Tests.Web
public static void Converting_Unsupported_Object_To_A_Udi_Returns_False()
{
// Arrange
- Udi.ResetUdiTypes();
+ UdiParser.ResetUdiTypes();
var clearlyWillNotConvertToGuid = new StringBuilder(0);
diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs
index aa8f735cf2..2381867616 100644
--- a/src/Umbraco.Web/Editors/EntityController.cs
+++ b/src/Umbraco.Web/Editors/EntityController.cs
@@ -509,7 +509,7 @@ namespace Umbraco.Web.Editors
throw new HttpResponseException(HttpStatusCode.NotFound);
}
- if (Udi.TryParse(id, out _))
+ if (UdiParser.TryParse(id, out _))
{
//Not supported currently
throw new HttpResponseException(HttpStatusCode.NotFound);
diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs
index 2cab9f4ebc..315abaa8ce 100644
--- a/src/Umbraco.Web/Editors/MediaController.cs
+++ b/src/Umbraco.Web/Editors/MediaController.cs
@@ -793,10 +793,9 @@ namespace Umbraco.Web.Editors
private int GetParentIdAsInt(string parentId, bool validatePermissions)
{
int intParentId;
- GuidUdi parentUdi;
// test for udi
- if (GuidUdi.TryParse(parentId, out parentUdi))
+ if (UdiParser.TryParse(parentId, out GuidUdi parentUdi))
{
parentId = parentUdi.Guid.ToString();
}
diff --git a/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs b/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs
index 4a3f6b43c3..9252899027 100644
--- a/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs
+++ b/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs
@@ -67,7 +67,7 @@ namespace Umbraco.Web.Editors
public RedirectUrlSearchResult RedirectUrlsForContentItem(string contentUdi)
{
var redirectsResult = new RedirectUrlSearchResult();
- if (GuidUdi.TryParse(contentUdi, out var guidIdi))
+ if (UdiParser.TryParse(contentUdi, out GuidUdi guidIdi))
{
var redirectUrlService = Services.RedirectUrlService;
var redirects = redirectUrlService.GetContentRedirectUrls(guidIdi.Guid);
diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs
index a56630d7c5..cf0e872c1a 100644
--- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs
+++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs
@@ -58,7 +58,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
var nodeIds = source.ToString()
.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
- .Select(Udi.Parse)
+ .Select(UdiParser.Parse)
.ToArray();
return nodeIds;
}
diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs
index 47f8797295..46ebbd0abb 100644
--- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs
+++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs
@@ -55,7 +55,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
{
var nodeIds = source.ToString()
.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
- .Select(Udi.Parse)
+ .Select(UdiParser.Parse)
.ToArray();
return nodeIds;
}
diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs
index b351cca972..c1844655a9 100644
--- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs
+++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs
@@ -326,7 +326,7 @@ namespace Umbraco.Web.Search
if (sb == null) throw new ArgumentNullException(nameof(sb));
if (entityService == null) throw new ArgumentNullException(nameof(entityService));
- Udi.TryParse(searchFrom, true, out var udi);
+ UdiParser.TryParse(searchFrom, true, out var udi);
searchFrom = udi == null ? searchFrom : entityService.GetId(udi).Result.ToString();
var entityPath = int.TryParse(searchFrom, out var searchFromId) && searchFromId > 0
diff --git a/src/Umbraco.Web/Templates/TemplateUtilities.cs b/src/Umbraco.Web/Templates/TemplateUtilities.cs
index 0f291a3c35..b98fedccbd 100644
--- a/src/Umbraco.Web/Templates/TemplateUtilities.cs
+++ b/src/Umbraco.Web/Templates/TemplateUtilities.cs
@@ -59,7 +59,7 @@ namespace Umbraco.Web.Templates
var id = tag.Groups[1].Value; //.Remove(tag.Groups[1].Value.Length - 1, 1);
//The id could be an int or a UDI
- if (Udi.TryParse(id, out var udi))
+ if (UdiParser.TryParse(id, out var udi))
{
var guidUdi = udi as GuidUdi;
if (guidUdi != null)
@@ -166,7 +166,7 @@ namespace Umbraco.Web.Templates
// - 4 = the data-udi attribute value
// - 5 = anything after group 4 until the image tag is closed
var udi = match.Groups[4].Value;
- if(udi.IsNullOrWhiteSpace() || GuidUdi.TryParse(udi, out var guidUdi) == false)
+ if(udi.IsNullOrWhiteSpace() || UdiParser.TryParse(udi, out GuidUdi guidUdi) == false)
{
return match.Value;
}
diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs
index 4efbc2adc8..faa6f56d2b 100644
--- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs
+++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs
@@ -491,7 +491,7 @@ namespace Umbraco.Web.Trees
{
return new Tuple(null, idInt);
}
- if (Udi.TryParse(id, out idUdi))
+ if (UdiParser.TryParse(id, out idUdi))
{
var guidUdi = idUdi as GuidUdi;
if (guidUdi != null)
@@ -523,7 +523,7 @@ namespace Umbraco.Web.Trees
{
entity = Services.EntityService.Get(idInt, UmbracoObjectType);
}
- else if (Udi.TryParse(s, out var idUdi))
+ else if (UdiParser.TryParse(s, out var idUdi))
{
var guidUdi = idUdi as GuidUdi;
entity = guidUdi != null ? Services.EntityService.Get(guidUdi.Guid, UmbracoObjectType) : null;
diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs
index 705d72f3dd..070e57ca5c 100644
--- a/src/Umbraco.Web/UmbracoHelper.cs
+++ b/src/Umbraco.Web/UmbracoHelper.cs
@@ -621,7 +621,7 @@ namespace Umbraco.Web
switch (id)
{
case string s:
- return Udi.TryParse(s, out guidId);
+ return UdiParser.TryParse(s, out guidId);
case Udi u:
guidId = u;
diff --git a/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs b/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs
index efee045890..9f163f553a 100644
--- a/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs
+++ b/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs
@@ -87,7 +87,7 @@ namespace Umbraco.Web.WebApi.Filters
{
nodeId = parsedId;
}
- else if (Udi.TryParse(argument, true, out Udi udi))
+ else if (UdiParser.TryParse(argument, true, out Udi udi))
{
// TODO: inject? we can't because this is an attribute but we could provide ctors and empty ctors that pass in the required services
nodeId = Current.Services.EntityService.GetId(udi).Result;