From 27fc3a9bb35d04a76e41bdb8adf6aac38506b8d5 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 13 Sep 2017 14:16:43 +1000 Subject: [PATCH 1/4] U4-10409 UDI Parsing should not require scanning assemblies --- src/Umbraco.Core/Udi.cs | 143 +++++++++++++++++++++++----------- src/Umbraco.Tests/UdiTests.cs | 5 +- 2 files changed, 97 insertions(+), 51 deletions(-) diff --git a/src/Umbraco.Core/Udi.cs b/src/Umbraco.Core/Udi.cs index 142bf025a9..13fc678b8e 100644 --- a/src/Umbraco.Core/Udi.cs +++ b/src/Umbraco.Core/Udi.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; +using System.Threading; using Umbraco.Core.Deploy; namespace Umbraco.Core @@ -15,7 +16,9 @@ namespace Umbraco.Core [TypeConverter(typeof(UdiTypeConverter))] public abstract class Udi : IComparable { - private static readonly Lazy> UdiTypes; + private static volatile bool _scanned = false; + private static readonly object ScanLocker = new object(); + private static readonly Lazy> KnownUdiTypes; private static readonly ConcurrentDictionary RootUdis = new ConcurrentDictionary(); internal readonly Uri UriValue; // internal for UdiRange @@ -42,7 +45,7 @@ namespace Umbraco.Core static Udi() { - UdiTypes = new Lazy>(() => + KnownUdiTypes = new Lazy>(() => { var result = new Dictionary(); @@ -65,26 +68,9 @@ namespace Umbraco.Core } } - // 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 = PluginManager.Current.ResolveTypes(); - 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; - } - } + //For non-known UDI types we'll try to parse a GUID and if that doesn't work, we'll decide that it's a string - return result; + return new ConcurrentDictionary(result); }); } @@ -122,6 +108,34 @@ namespace Umbraco.Core return ParseInternal(s, true, out udi); } + private static UdiType GetUdiType(Uri uri, out string path) + { + path = uri.AbsolutePath.TrimStart('/'); + + UdiType udiType; + if (KnownUdiTypes.Value.TryGetValue(uri.Host, out udiType)) + { + return udiType; + } + + //if it's empty and it's not in our known list then we don't know + if (path.IsNullOrWhiteSpace()) + return UdiType.Unknown; + + //try to parse into a Guid + Guid guidId; + if (Guid.TryParse(path, out guidId)) + { + //add it to our known list + KnownUdiTypes.Value.TryAdd(uri.Host, UdiType.GuidUdi); + return UdiType.GuidUdi; + } + + //add it to our known list - if it's not a GUID then it must a string + KnownUdiTypes.Value.TryAdd(uri.Host, UdiType.StringUdi); + return UdiType.StringUdi; + } + private static bool ParseInternal(string s, bool tryParse, out Udi udi) { udi = null; @@ -134,21 +148,22 @@ namespace Umbraco.Core throw new FormatException(string.Format("String \"{0}\" is not a valid udi.", s)); } - var entityType = uri.Host; - UdiType udiType; - if (UdiTypes.Value.TryGetValue(entityType, out udiType) == false) + string path; + var udiType = GetUdiType(uri, out path); + + if (path.IsNullOrWhiteSpace()) { - if (tryParse) return false; - throw new FormatException(string.Format("Unknown entity type \"{0}\".", entityType)); + //in this case it's because the path is empty which indicates we need to return the root udi + udi = GetRootUdi(uri.Host); + return true; } - var path = uri.AbsolutePath.TrimStart('/'); + + //This should never happen, if it's an empty path that would have been taken care of above + if (udiType == UdiType.Unknown) + throw new InvalidOperationException("Internal error."); + if (udiType == UdiType.GuidUdi) { - if (path == string.Empty) - { - udi = GetRootUdi(uri.Host); - return true; - } Guid guid; if (Guid.TryParse(path, out guid) == false) { @@ -160,7 +175,7 @@ namespace Umbraco.Core } if (udiType == UdiType.StringUdi) { - udi = path == string.Empty ? GetRootUdi(uri.Host) : new StringUdi(uri.Host, Uri.UnescapeDataString(path)); + udi = new StringUdi(uri.Host, Uri.UnescapeDataString(path)); return true; } if (tryParse) return false; @@ -169,10 +184,12 @@ namespace Umbraco.Core private static Udi GetRootUdi(string entityType) { + ScanAllUdiTypes(); + return RootUdis.GetOrAdd(entityType, x => { UdiType udiType; - if (UdiTypes.Value.TryGetValue(x, out udiType) == false) + if (KnownUdiTypes.Value.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) @@ -180,6 +197,48 @@ namespace Umbraco.Core }); } + /// + /// When required scan assemblies for known UDI types based on instances + /// + /// + /// This is only required when needing to resolve root udis + /// + private static void ScanAllUdiTypes() + { + 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 = PluginManager.Current.ResolveTypes(); + 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) + { + KnownUdiTypes.Value.TryAdd(item.Key, item.Value); + } + + _scanned = true; + } + } + /// /// Creates a root Udi for an entity type. /// @@ -197,14 +256,9 @@ namespace Umbraco.Core /// The identifier. /// The string Udi for the entity type and identifier. public static Udi Create(string entityType, string id) - { - UdiType udiType; - if (UdiTypes.Value.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); } @@ -216,12 +270,7 @@ namespace Umbraco.Core /// The identifier. /// The Guid Udi for the entity type and identifier. public static Udi Create(string entityType, Guid id) - { - UdiType udiType; - if (UdiTypes.Value.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); @@ -230,7 +279,7 @@ namespace Umbraco.Core internal static Udi Create(Uri uri) { UdiType udiType; - if (UdiTypes.Value.TryGetValue(uri.Host, out udiType) == false) + if (KnownUdiTypes.Value.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); diff --git a/src/Umbraco.Tests/UdiTests.cs b/src/Umbraco.Tests/UdiTests.cs index 24db54a98f..88eba62baf 100644 --- a/src/Umbraco.Tests/UdiTests.cs +++ b/src/Umbraco.Tests/UdiTests.cs @@ -147,10 +147,7 @@ namespace Umbraco.Tests var udi = Udi.Create(Constants.UdiEntityType.AnyGuid, guid); Assert.AreEqual(Constants.UdiEntityType.AnyGuid, udi.EntityType); Assert.AreEqual(guid, ((GuidUdi)udi).Guid); - - Assert.Throws(() => Udi.Create(Constants.UdiEntityType.AnyString, guid)); - Assert.Throws(() => Udi.Create(Constants.UdiEntityType.AnyGuid, "foo")); - Assert.Throws(() => Udi.Create("barf", "foo")); + } [Test] From add0685d3f1efdf3bd6278a5abec4c7908d43787 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 13 Sep 2017 09:35:17 +0200 Subject: [PATCH 2/4] U4-10409 - udi parsing --- src/Umbraco.Core/GuidUdi.cs | 16 +++--- src/Umbraco.Core/StringUdi.cs | 12 ++--- src/Umbraco.Core/Udi.cs | 79 +++++++++++++--------------- src/Umbraco.Core/UdiEntityType.cs | 87 ++++++++++++++++--------------- src/Umbraco.Core/UdiRange.cs | 4 +- src/Umbraco.Tests/UdiTests.cs | 76 +++++++++++++++++++++++++-- 6 files changed, 170 insertions(+), 104 deletions(-) diff --git a/src/Umbraco.Core/GuidUdi.cs b/src/Umbraco.Core/GuidUdi.cs index 7a740ef58f..5f4f119901 100644 --- a/src/Umbraco.Core/GuidUdi.cs +++ b/src/Umbraco.Core/GuidUdi.cs @@ -32,7 +32,11 @@ namespace Umbraco.Core public GuidUdi(Uri uriValue) : base(uriValue) { - Guid = Guid.Parse(uriValue.AbsolutePath.TrimStart('/')); + Guid guid; + if (Guid.TryParse(uriValue.AbsolutePath.TrimStart('/'), out guid) == false) + throw new FormatException("Url \"" + uriValue + "\" is not a guid entity id."); + + Guid = guid; } /// @@ -43,16 +47,17 @@ namespace Umbraco.Core public new static GuidUdi Parse(string s) { var udi = Udi.Parse(s); - if (!(udi is GuidUdi)) + if (udi is GuidUdi == false) throw new FormatException("String \"" + s + "\" is not a guid entity id."); - return (GuidUdi)udi; + + return (GuidUdi) udi; } public static bool TryParse(string s, out GuidUdi udi) { Udi tmp; udi = null; - if (!TryParse(s, out tmp)) return false; + if (TryParse(s, out tmp) == false) return false; udi = tmp as GuidUdi; return udi != null; } @@ -75,10 +80,9 @@ namespace Umbraco.Core get { return Guid == Guid.Empty; } } - /// public GuidUdi EnsureClosed() { - base.EnsureNotRoot(); + EnsureNotRoot(); return this; } } diff --git a/src/Umbraco.Core/StringUdi.cs b/src/Umbraco.Core/StringUdi.cs index 59eb40af7e..7df791219a 100644 --- a/src/Umbraco.Core/StringUdi.cs +++ b/src/Umbraco.Core/StringUdi.cs @@ -57,17 +57,18 @@ namespace Umbraco.Core public new static StringUdi Parse(string s) { var udi = Udi.Parse(s); - if (!(udi is StringUdi)) + if (udi is StringUdi == false) throw new FormatException("String \"" + s + "\" is not a string entity id."); - return (StringUdi)udi; + + return (StringUdi) udi; } public static bool TryParse(string s, out StringUdi udi) { udi = null; Udi tmp; - if (!TryParse(s, out tmp) || !(tmp is StringUdi)) return false; - udi = (StringUdi)tmp; + if (TryParse(s, out tmp) == false || tmp is StringUdi == false) return false; + udi = (StringUdi) tmp; return true; } @@ -77,10 +78,9 @@ namespace Umbraco.Core get { return Id == string.Empty; } } - /// public StringUdi EnsureClosed() { - base.EnsureNotRoot(); + EnsureNotRoot(); return this; } } diff --git a/src/Umbraco.Core/Udi.cs b/src/Umbraco.Core/Udi.cs index 13fc678b8e..74e77e3fd2 100644 --- a/src/Umbraco.Core/Udi.cs +++ b/src/Umbraco.Core/Udi.cs @@ -3,8 +3,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Linq; -using System.Reflection; -using System.Threading; using Umbraco.Core.Deploy; namespace Umbraco.Core @@ -16,9 +14,15 @@ namespace Umbraco.Core [TypeConverter(typeof(UdiTypeConverter))] public abstract class Udi : IComparable { - private static volatile bool _scanned = false; + // 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 readonly Lazy> KnownUdiTypes; + private static readonly Lazy> KnownUdiTypes; private static readonly ConcurrentDictionary RootUdis = new ConcurrentDictionary(); internal readonly Uri UriValue; // internal for UdiRange @@ -45,33 +49,9 @@ namespace Umbraco.Core static Udi() { - KnownUdiTypes = new Lazy>(() => - { - var result = new Dictionary(); - - // known types: - foreach (var fi in typeof(Constants.UdiEntityType).GetFields(BindingFlags.Public | BindingFlags.Static)) - { - // IsLiteral determines if its value is written at - // compile time and not changeable - // IsInitOnly determine if the field can be set - // in the body of the constructor - // for C# a field which is readonly keyword would have both true - // but a const field would have only IsLiteral equal to true - if (fi.IsLiteral && fi.IsInitOnly == false) - { - var udiType = fi.GetCustomAttribute(); - - if (udiType == null) - throw new InvalidOperationException("All Constants listed in UdiEntityType must be attributed with " + typeof(Constants.UdiTypeAttribute)); - result[fi.GetValue(null).ToString()] = udiType.UdiType; - } - } - - //For non-known UDI types we'll try to parse a GUID and if that doesn't work, we'll decide that it's a string - - return new ConcurrentDictionary(result); - }); + // initialize with known (built-in) Udi types + // for non-known Udi types we'll try to parse a GUID and if that doesn't work, we'll decide that it's a string + KnownUdiTypes = new Lazy>(() => new ConcurrentDictionary(Constants.UdiEntityType.GetTypes())); } /// @@ -117,12 +97,13 @@ namespace Umbraco.Core { return udiType; } - - //if it's empty and it's not in our known list then we don't know + + // if it's empty and it's not in our known list then we don't know if (path.IsNullOrWhiteSpace()) return UdiType.Unknown; - //try to parse into a Guid + // try to parse into a Guid + // (note: root udis use Guid.Empty so this is ok) Guid guidId; if (Guid.TryParse(path, out guidId)) { @@ -131,7 +112,7 @@ namespace Umbraco.Core return UdiType.GuidUdi; } - //add it to our known list - if it's not a GUID then it must a string + // add it to our known list - if it's not a GUID then it must a string KnownUdiTypes.Value.TryAdd(uri.Host, UdiType.StringUdi); return UdiType.StringUdi; } @@ -148,19 +129,21 @@ namespace Umbraco.Core throw new FormatException(string.Format("String \"{0}\" is not a valid udi.", s)); } + // if it's a known entity type, GetUdiType will return it + // else it will try to guess based on the path, and register the type as known string path; var udiType = GetUdiType(uri, out path); if (path.IsNullOrWhiteSpace()) { - //in this case it's because the path is empty which indicates we need to return the root udi + // path is empty which indicates we need to return the root udi udi = GetRootUdi(uri.Host); return true; } - //This should never happen, if it's an empty path that would have been taken care of above + // if the path is not empty, type should not be unknown if (udiType == UdiType.Unknown) - throw new InvalidOperationException("Internal error."); + throw new FormatException(string.Format("Could not determine the Udi type for string \"{0}\".", s)); if (udiType == UdiType.GuidUdi) { @@ -256,10 +239,14 @@ namespace Umbraco.Core /// The identifier. /// The string Udi for the entity type and identifier. public static Udi Create(string entityType, string id) - { + { + UdiType udiType; + if (KnownUdiTypes.Value.TryGetValue(entityType, out udiType) && udiType != UdiType.StringUdi) + throw new InvalidOperationException(string.Format("Entity type \"{0}\" is not a StringUdi.", entityType)); + if (string.IsNullOrWhiteSpace(id)) throw new ArgumentException("Value cannot be null or whitespace.", "id"); - + return new StringUdi(entityType, id); } @@ -270,7 +257,11 @@ namespace Umbraco.Core /// The identifier. /// The Guid Udi for the entity type and identifier. public static Udi Create(string entityType, Guid id) - { + { + UdiType udiType; + if (KnownUdiTypes.Value.TryGetValue(entityType, out udiType) && udiType != UdiType.GuidUdi) + throw new InvalidOperationException(string.Format("Entity type \"{0}\" is not a GuidUdi.", entityType)); + if (id == default(Guid)) throw new ArgumentException("Cannot be an empty guid.", "id"); return new GuidUdi(entityType, id); @@ -278,9 +269,13 @@ namespace Umbraco.Core 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 (KnownUdiTypes.Value.TryGetValue(uri.Host, out udiType) == false) - throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", uri.Host), "uri"); + return Parse(uri.ToString()); + if (udiType == UdiType.GuidUdi) return new GuidUdi(uri); if (udiType == UdiType.GuidUdi) diff --git a/src/Umbraco.Core/UdiEntityType.cs b/src/Umbraco.Core/UdiEntityType.cs index 8fb5d99338..691cba4f7d 100644 --- a/src/Umbraco.Core/UdiEntityType.cs +++ b/src/Umbraco.Core/UdiEntityType.cs @@ -1,9 +1,9 @@ using System; +using System.Collections.Generic; using Umbraco.Core.Models; namespace Umbraco.Core { - public static partial class Constants { /// @@ -13,81 +13,93 @@ namespace Umbraco.Core /// but entity types are strings and so can be extended beyond what is defined here. public static class UdiEntityType { - [UdiType(UdiType.Unknown)] + // note: const fields in this class MUST be consistent with what GetTypes returns + // this is validated by UdiTests.ValidateUdiEntityType + + internal static Dictionary GetTypes() + { + return new Dictionary + { + { Unknown, UdiType.Unknown }, + + { AnyGuid, UdiType.GuidUdi }, + { Document, UdiType.GuidUdi }, + { Media, UdiType.GuidUdi }, + { Member, UdiType.GuidUdi }, + { DictionaryItem, UdiType.GuidUdi }, + { Macro, UdiType.GuidUdi }, + { Template, UdiType.GuidUdi }, + { DocumentType, UdiType.GuidUdi }, + { DocumentTypeContainer, UdiType.GuidUdi }, + { MediaType, UdiType.GuidUdi }, + { MediaTypeContainer, UdiType.GuidUdi }, + { DataType, UdiType.GuidUdi }, + { DataTypeContainer, UdiType.GuidUdi }, + { MemberType, UdiType.GuidUdi }, + { MemberGroup, UdiType.GuidUdi }, + { RelationType, UdiType.GuidUdi }, + { FormsForm, UdiType.GuidUdi }, + { FormsPreValue, UdiType.GuidUdi }, + { FormsDataSource, UdiType.GuidUdi }, + + { AnyString, UdiType.StringUdi}, + { Language, UdiType.StringUdi}, + { MacroScript, UdiType.StringUdi}, + { MediaFile, UdiType.StringUdi}, + { TemplateFile, UdiType.StringUdi}, + { Script, UdiType.StringUdi}, + { PartialView, UdiType.StringUdi}, + { PartialViewMacro, UdiType.StringUdi}, + { Stylesheet, UdiType.StringUdi}, + { UserControl, UdiType.StringUdi}, + { Xslt, UdiType.StringUdi}, + }; + } + public const string Unknown = "unknown"; // guid entity types - [UdiType(UdiType.GuidUdi)] public const string AnyGuid = "any-guid"; // that one is for tests - [UdiType(UdiType.GuidUdi)] public const string Document = "document"; - [UdiType(UdiType.GuidUdi)] public const string Media = "media"; - [UdiType(UdiType.GuidUdi)] public const string Member = "member"; - [UdiType(UdiType.GuidUdi)] public const string DictionaryItem = "dictionary-item"; - [UdiType(UdiType.GuidUdi)] public const string Macro = "macro"; - [UdiType(UdiType.GuidUdi)] public const string Template = "template"; - [UdiType(UdiType.GuidUdi)] public const string DocumentType = "document-type"; - [UdiType(UdiType.GuidUdi)] public const string DocumentTypeContainer = "document-type-container"; - [UdiType(UdiType.GuidUdi)] public const string MediaType = "media-type"; - [UdiType(UdiType.GuidUdi)] public const string MediaTypeContainer = "media-type-container"; - [UdiType(UdiType.GuidUdi)] public const string DataType = "data-type"; - [UdiType(UdiType.GuidUdi)] public const string DataTypeContainer = "data-type-container"; - [UdiType(UdiType.GuidUdi)] public const string MemberType = "member-type"; - [UdiType(UdiType.GuidUdi)] public const string MemberGroup = "member-group"; - [UdiType(UdiType.GuidUdi)] public const string RelationType = "relation-type"; // forms - [UdiType(UdiType.GuidUdi)] public const string FormsForm = "forms-form"; - [UdiType(UdiType.GuidUdi)] public const string FormsPreValue = "forms-prevalue"; - [UdiType(UdiType.GuidUdi)] public const string FormsDataSource = "forms-datasource"; // string entity types - [UdiType(UdiType.StringUdi)] public const string AnyString = "any-string"; // that one is for tests - [UdiType(UdiType.StringUdi)] public const string Language = "language"; - [UdiType(UdiType.StringUdi)] public const string MacroScript = "macroscript"; - [UdiType(UdiType.StringUdi)] public const string MediaFile = "media-file"; - [UdiType(UdiType.StringUdi)] public const string TemplateFile = "template-file"; - [UdiType(UdiType.StringUdi)] public const string Script = "script"; - [UdiType(UdiType.StringUdi)] public const string Stylesheet = "stylesheet"; - [UdiType(UdiType.StringUdi)] public const string PartialView = "partial-view"; - [UdiType(UdiType.StringUdi)] public const string PartialViewMacro = "partial-view-macro"; - [UdiType(UdiType.StringUdi)] public const string UserControl = "usercontrol"; - [UdiType(UdiType.StringUdi)] public const string Xslt = "xslt"; public static string FromUmbracoObjectType(UmbracoObjectTypes umbracoObjectType) @@ -179,16 +191,5 @@ namespace Umbraco.Core string.Format("EntityType \"{0}\" does not have a matching UmbracoObjectType.", entityType)); } } - - [AttributeUsage(AttributeTargets.Field)] - internal class UdiTypeAttribute : Attribute - { - public UdiType UdiType { get; private set; } - - public UdiTypeAttribute(UdiType udiType) - { - UdiType = udiType; - } - } } } \ No newline at end of file diff --git a/src/Umbraco.Core/UdiRange.cs b/src/Umbraco.Core/UdiRange.cs index a708220066..f87887a6f8 100644 --- a/src/Umbraco.Core/UdiRange.cs +++ b/src/Umbraco.Core/UdiRange.cs @@ -62,8 +62,8 @@ namespace Umbraco.Core { Uri uri; - if (!Uri.IsWellFormedUriString(s, UriKind.Absolute) - || !Uri.TryCreate(s, UriKind.Absolute, out 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 range.", s)); diff --git a/src/Umbraco.Tests/UdiTests.cs b/src/Umbraco.Tests/UdiTests.cs index 88eba62baf..9c46b27a89 100644 --- a/src/Umbraco.Tests/UdiTests.cs +++ b/src/Umbraco.Tests/UdiTests.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Reflection; using Newtonsoft.Json; using NUnit.Framework; using Umbraco.Core; @@ -11,7 +12,7 @@ namespace Umbraco.Tests public class UdiTests { [Test] - public void StringEntityCtorTest() + public void StringUdiCtorTest() { var udi = new StringUdi(Constants.UdiEntityType.AnyString, "test-id"); Assert.AreEqual(Constants.UdiEntityType.AnyString, udi.EntityType); @@ -20,7 +21,7 @@ namespace Umbraco.Tests } [Test] - public void StringEntityParseTest() + public void StringUdiParseTest() { var udi = Udi.Parse("umb://" + Constants.UdiEntityType.AnyString + "/test-id"); Assert.AreEqual(Constants.UdiEntityType.AnyString, udi.EntityType); @@ -29,6 +30,9 @@ namespace Umbraco.Tests Assert.IsNotNull(stringEntityId); Assert.AreEqual("test-id", stringEntityId.Id); Assert.AreEqual("umb://" + Constants.UdiEntityType.AnyString + "/test-id", udi.ToString()); + + udi = Udi.Parse("umb://" + Constants.UdiEntityType.AnyString + "/DA845952BE474EE9BD6F6194272AC750"); + Assert.IsInstanceOf(udi); } [Test] @@ -83,7 +87,7 @@ namespace Umbraco.Tests } [Test] - public void GuidEntityCtorTest() + public void GuidUdiCtorTest() { var guid = Guid.NewGuid(); var udi = new GuidUdi(Constants.UdiEntityType.AnyGuid, guid); @@ -93,7 +97,7 @@ namespace Umbraco.Tests } [Test] - public void GuidEntityParseTest() + public void GuidUdiParseTest() { var guid = Guid.NewGuid(); var s = "umb://" + Constants.UdiEntityType.AnyGuid + "/" + guid.ToString("N"); @@ -147,7 +151,43 @@ namespace Umbraco.Tests var udi = Udi.Create(Constants.UdiEntityType.AnyGuid, guid); Assert.AreEqual(Constants.UdiEntityType.AnyGuid, udi.EntityType); Assert.AreEqual(guid, ((GuidUdi)udi).Guid); - + + // *not* testing whether Udi.Create(type, invalidValue) throws + // because we don't throw anymore - see U4-10409 + } + + [Test] + public void RootUdiTest() + { + var stringUdi = new StringUdi(Constants.UdiEntityType.AnyString, string.Empty); + Assert.IsTrue(stringUdi.IsRoot); + Assert.AreEqual("umb://any-string/", stringUdi.ToString()); + + var guidUdi = new GuidUdi(Constants.UdiEntityType.AnyGuid, Guid.Empty); + Assert.IsTrue(guidUdi.IsRoot); + Assert.AreEqual("umb://any-guid/00000000000000000000000000000000", guidUdi.ToString()); + + var udi = Udi.Parse("umb://any-string/"); + Assert.IsTrue(udi.IsRoot); + Assert.IsInstanceOf(udi); + + udi = Udi.Parse("umb://any-guid/00000000000000000000000000000000"); + Assert.IsTrue(udi.IsRoot); + Assert.IsInstanceOf(udi); + + udi = Udi.Parse("umb://any-guid/"); + Assert.IsTrue(udi.IsRoot); + Assert.IsInstanceOf(udi); + } + + [Test] + public void NotKnownTypeTest() + { + var udi1 = Udi.Parse("umb://not-known-1/DA845952BE474EE9BD6F6194272AC750"); + Assert.IsInstanceOf(udi1); + + var udi2 = Udi.Parse("umb://not-known-2/this-is-not-a-guid"); + Assert.IsInstanceOf(udi2); } [Test] @@ -199,5 +239,31 @@ namespace Umbraco.Tests Assert.AreEqual(string.Format("umb://any-guid/{0:N}", guid), drange.Udi.UriValue.ToString()); Assert.AreEqual(Constants.DeploySelector.ChildrenOfThis, drange.Selector); } + + [Test] + public void ValidateUdiEntityType() + { + var types = Constants.UdiEntityType.GetTypes(); + + foreach (var fi in typeof (Constants.UdiEntityType).GetFields(BindingFlags.Public | BindingFlags.Static)) + { + // IsLiteral determines if its value is written at + // compile time and not changeable + // IsInitOnly determine if the field can be set + // in the body of the constructor + // for C# a field which is readonly keyword would have both true + // but a const field would have only IsLiteral equal to true + if (fi.IsLiteral && fi.IsInitOnly == false) + { + var value = fi.GetValue(null).ToString(); + + if (types.ContainsKey(value) == false) + Assert.Fail("Error in class Constants.UdiEntityType, type \"{0}\" is not declared by GetTypes.", value); + types.Remove(value); + } + } + + Assert.AreEqual(0, types.Count, "Error in class Constants.UdiEntityType, GetTypes declares types that don't exist ({0}).", string.Join(",", types.Keys.Select(x => "\"" + x + "\""))); + } } } \ No newline at end of file From ef91e152b4543d731448ee75eb54cb2af806e3a5 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 13 Sep 2017 11:50:37 +0200 Subject: [PATCH 3/4] U4-10409 - udi parsing and assembly scanning --- .../KnownTypeUdiJsonConverter.cs | 26 +++ .../Serialization/UdiJsonConverter.cs | 3 +- src/Umbraco.Core/Udi.cs | 171 ++++++++++++------ src/Umbraco.Core/Umbraco.Core.csproj | 1 + src/Umbraco.Tests/UdiTests.cs | 93 +++++++++- src/Umbraco.Web.UI/config/Dashboard.config | 10 + .../config/splashes/noNodes.aspx | 158 ++++++++++++---- src/Umbraco.Web.UI/config/trees.config | 1 + 8 files changed, 359 insertions(+), 104 deletions(-) create mode 100644 src/Umbraco.Core/Serialization/KnownTypeUdiJsonConverter.cs diff --git a/src/Umbraco.Core/Serialization/KnownTypeUdiJsonConverter.cs b/src/Umbraco.Core/Serialization/KnownTypeUdiJsonConverter.cs new file mode 100644 index 0000000000..e6473e7f8e --- /dev/null +++ b/src/Umbraco.Core/Serialization/KnownTypeUdiJsonConverter.cs @@ -0,0 +1,26 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Umbraco.Core.Serialization +{ + public class KnownTypeUdiJsonConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return typeof(Udi).IsAssignableFrom(objectType); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(value.ToString()); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var jo = JToken.ReadFrom(reader); + var val = jo.ToObject(); + return val == null ? null : Udi.Parse(val, true); + } + } +} diff --git a/src/Umbraco.Core/Serialization/UdiJsonConverter.cs b/src/Umbraco.Core/Serialization/UdiJsonConverter.cs index ff62535825..f3dc678ce6 100644 --- a/src/Umbraco.Core/Serialization/UdiJsonConverter.cs +++ b/src/Umbraco.Core/Serialization/UdiJsonConverter.cs @@ -4,12 +4,11 @@ using Newtonsoft.Json.Linq; namespace Umbraco.Core.Serialization { - public class UdiJsonConverter : JsonConverter { public override bool CanConvert(Type objectType) { - return typeof(Udi).IsAssignableFrom(objectType); + return typeof (Udi).IsAssignableFrom(objectType); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) diff --git a/src/Umbraco.Core/Udi.cs b/src/Umbraco.Core/Udi.cs index 74e77e3fd2..a76c692661 100644 --- a/src/Umbraco.Core/Udi.cs +++ b/src/Umbraco.Core/Udi.cs @@ -22,7 +22,7 @@ namespace Umbraco.Core private static volatile bool _scanned; private static readonly object ScanLocker = new object(); - private static readonly Lazy> KnownUdiTypes; + private static ConcurrentDictionary _udiTypes; private static readonly ConcurrentDictionary RootUdis = new ConcurrentDictionary(); internal readonly Uri UriValue; // internal for UdiRange @@ -50,8 +50,15 @@ namespace Umbraco.Core static Udi() { // initialize with known (built-in) Udi types - // for non-known Udi types we'll try to parse a GUID and if that doesn't work, we'll decide that it's a string - KnownUdiTypes = new Lazy>(() => new ConcurrentDictionary(Constants.UdiEntityType.GetTypes())); + // we will add scanned types later on + _udiTypes = new ConcurrentDictionary(Constants.UdiEntityType.GetTypes()); + } + + // for tests, totally unsafe + internal static void ResetUdiTypes() + { + _udiTypes = new ConcurrentDictionary(Constants.UdiEntityType.GetTypes()); + _scanned = false; } /// @@ -79,46 +86,65 @@ namespace Umbraco.Core public static Udi Parse(string s) { Udi udi; - ParseInternal(s, false, out 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, out udi); + return ParseInternal(s, true, false, out udi); } - private static UdiType GetUdiType(Uri uri, out string path) + /// + /// 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) { - path = uri.AbsolutePath.TrimStart('/'); - - UdiType udiType; - if (KnownUdiTypes.Value.TryGetValue(uri.Host, out udiType)) - { - return udiType; - } - - // if it's empty and it's not in our known list then we don't know - if (path.IsNullOrWhiteSpace()) - return UdiType.Unknown; - - // try to parse into a Guid - // (note: root udis use Guid.Empty so this is ok) - Guid guidId; - if (Guid.TryParse(path, out guidId)) - { - //add it to our known list - KnownUdiTypes.Value.TryAdd(uri.Host, UdiType.GuidUdi); - return UdiType.GuidUdi; - } - - // add it to our known list - if it's not a GUID then it must a string - KnownUdiTypes.Value.TryAdd(uri.Host, UdiType.StringUdi); - return UdiType.StringUdi; + return ParseInternal(s, true, knownTypes, out udi); } - private static bool ParseInternal(string s, bool tryParse, out Udi udi) + private static bool ParseInternal(string s, bool tryParse, bool knownTypes, out Udi udi) { + if (knownTypes == false) + EnsureScanForUdiTypes(); + udi = null; Uri uri; @@ -129,24 +155,30 @@ namespace Umbraco.Core throw new FormatException(string.Format("String \"{0}\" is not a valid udi.", s)); } - // if it's a known entity type, GetUdiType will return it - // else it will try to guess based on the path, and register the type as known - string path; - var udiType = GetUdiType(uri, out path); - - if (path.IsNullOrWhiteSpace()) + var entityType = uri.Host; + UdiType udiType; + if (_udiTypes.TryGetValue(entityType, out udiType) == false) { - // path is empty which indicates we need to return the root udi - udi = GetRootUdi(uri.Host); - return true; + 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)); } - // if the path is not empty, type should not be unknown - if (udiType == UdiType.Unknown) - throw new FormatException(string.Format("Could not determine the Udi type for string \"{0}\".", s)); + 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) { @@ -156,23 +188,25 @@ namespace Umbraco.Core udi = new GuidUdi(uri.Host, guid); return true; } + if (udiType == UdiType.StringUdi) { - udi = new StringUdi(uri.Host, Uri.UnescapeDataString(path)); + udi = path == string.Empty ? GetRootUdi(uri.Host) : new StringUdi(uri.Host, Uri.UnescapeDataString(path)); return true; } + if (tryParse) return false; - throw new InvalidOperationException("Internal error."); + throw new InvalidOperationException(string.Format("Invalid udi type \"{0}\".", udiType)); } private static Udi GetRootUdi(string entityType) { - ScanAllUdiTypes(); + EnsureScanForUdiTypes(); return RootUdis.GetOrAdd(entityType, x => { UdiType udiType; - if (KnownUdiTypes.Value.TryGetValue(x, out udiType) == false) + 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) @@ -186,7 +220,7 @@ namespace Umbraco.Core /// /// This is only required when needing to resolve root udis /// - private static void ScanAllUdiTypes() + private static void EnsureScanForUdiTypes() { if (_scanned) return; @@ -212,11 +246,9 @@ namespace Umbraco.Core } } - //merge these into the known list + // merge these into the known list foreach (var item in result) - { - KnownUdiTypes.Value.TryAdd(item.Key, item.Value); - } + _udiTypes.TryAdd(item.Key, item.Value); _scanned = true; } @@ -241,11 +273,13 @@ namespace Umbraco.Core public static Udi Create(string entityType, string id) { UdiType udiType; - if (KnownUdiTypes.Value.TryGetValue(entityType, out udiType) && udiType != UdiType.StringUdi) - throw new InvalidOperationException(string.Format("Entity type \"{0}\" is not a StringUdi.", entityType)); + 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); } @@ -259,11 +293,14 @@ namespace Umbraco.Core public static Udi Create(string entityType, Guid id) { UdiType udiType; - if (KnownUdiTypes.Value.TryGetValue(entityType, out udiType) && udiType != UdiType.GuidUdi) - throw new InvalidOperationException(string.Format("Entity type \"{0}\" is not a GuidUdi.", entityType)); + 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); } @@ -273,13 +310,14 @@ namespace Umbraco.Core // else fallback to parsing the string (and guess the type) UdiType udiType; - if (KnownUdiTypes.Value.TryGetValue(uri.Host, out udiType) == false) - return Parse(uri.ToString()); + 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)); } @@ -328,6 +366,19 @@ namespace Umbraco.Core { 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/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 3850fb98b0..dc5bf4480b 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -644,6 +644,7 @@ + diff --git a/src/Umbraco.Tests/UdiTests.cs b/src/Umbraco.Tests/UdiTests.cs index 9c46b27a89..75f4536fd4 100644 --- a/src/Umbraco.Tests/UdiTests.cs +++ b/src/Umbraco.Tests/UdiTests.cs @@ -1,9 +1,11 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Reflection; using Newtonsoft.Json; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Deploy; using Umbraco.Core.Serialization; namespace Umbraco.Tests @@ -180,16 +182,6 @@ namespace Umbraco.Tests Assert.IsInstanceOf(udi); } - [Test] - public void NotKnownTypeTest() - { - var udi1 = Udi.Parse("umb://not-known-1/DA845952BE474EE9BD6F6194272AC750"); - Assert.IsInstanceOf(udi1); - - var udi2 = Udi.Parse("umb://not-known-2/this-is-not-a-guid"); - Assert.IsInstanceOf(udi2); - } - [Test] public void RangeTest() { @@ -265,5 +257,86 @@ namespace Umbraco.Tests Assert.AreEqual(0, types.Count, "Error in class Constants.UdiEntityType, GetTypes declares types that don't exist ({0}).", string.Join(",", types.Keys.Select(x => "\"" + x + "\""))); } + + [Test] + public void KnownTypes() + { + Udi udi; + + // cannot parse an unknown type, udi is null + // this will scan + Assert.IsFalse(Udi.TryParse("umb://whatever/1234", out udi)); + Assert.IsNull(udi); + + Udi.ResetUdiTypes(); + + // unless we want to know + Assert.IsFalse(Udi.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(); + + // not known + Assert.IsFalse(Udi.TryParse("umb://foo/A87F65C8D6B94E868F6949BA92C93045", true, out udi)); + Assert.AreEqual(Constants.UdiEntityType.Unknown, udi.EntityType); + Assert.AreEqual("Umbraco.Core.Udi+UnknownTypeUdi", udi.GetType().FullName); + + // scanned + Assert.IsTrue(Udi.TryParse("umb://foo/A87F65C8D6B94E868F6949BA92C93045", out udi)); + Assert.IsInstanceOf(udi); + + // known + Assert.IsTrue(Udi.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); + Assert.IsNotNull(method); + } + + [UdiDefinition("foo", UdiType.GuidUdi)] + public class FooConnector : IServiceConnector + { + public IArtifact GetArtifact(Udi udi) + { + throw new NotImplementedException(); + } + + public IArtifact GetArtifact(object entity) + { + throw new NotImplementedException(); + } + + public ArtifactDeployState ProcessInit(IArtifact art, IDeployContext context) + { + throw new NotImplementedException(); + } + + public void Process(ArtifactDeployState dart, IDeployContext context, int pass) + { + throw new NotImplementedException(); + } + + public void Explode(UdiRange range, List udis) + { + throw new NotImplementedException(); + } + + public NamedUdiRange GetRange(Udi udi, string selector) + { + throw new NotImplementedException(); + } + + public NamedUdiRange GetRange(string entityType, string sid, string selector) + { + throw new NotImplementedException(); + } + + public bool Compare(IArtifact art1, IArtifact art2, ICollection differences = null) + { + throw new NotImplementedException(); + } + } } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/Dashboard.config b/src/Umbraco.Web.UI/config/Dashboard.config index 322551545d..c8c7e32f27 100644 --- a/src/Umbraco.Web.UI/config/Dashboard.config +++ b/src/Umbraco.Web.UI/config/Dashboard.config @@ -119,4 +119,14 @@ +
+ + content + + + + /App_Plugins/Deploy/views/dashboards/dashboard.html + + +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/splashes/noNodes.aspx b/src/Umbraco.Web.UI/config/splashes/noNodes.aspx index d46ecd6113..64d4446e3d 100644 --- a/src/Umbraco.Web.UI/config/splashes/noNodes.aspx +++ b/src/Umbraco.Web.UI/config/splashes/noNodes.aspx @@ -1,7 +1,7 @@ -<%@ Page Language="C#" AutoEventWireup="True" Inherits="Umbraco.Web.UI.Config.Splashes.NoNodes" CodeBehind="NoNodes.aspx.cs" %> -<%@ Import Namespace="Umbraco.Core.Configuration" %> +<%@ Page Language="C#" AutoEventWireup="true"%> <%@ Import Namespace="Umbraco.Core.IO" %> - +<%@ Import Namespace="Umbraco.Deploy.UI" %> +<%@ Import Namespace="Umbraco.Web" %> @@ -11,51 +11,145 @@ - + - + + + + + + + +<% if(HttpContext.Current.Request.IsLocal == false){ %>
-
-
- +
+
+ -

Welcome to your Umbraco installation

-

You're seeing this wonderful page because your website doesn't contain any published content yet.

+

Welcome to your Umbraco installation

+

You're seeing the wonderful page because your website doesn't contain any published content yet.

- + -
-
-

Easy start with Umbraco.tv

-

We have created a bunch of 'how-to' videos, to get you easily started with Umbraco. Learn how to build projects in just a couple of minutes. Easiest CMS in the world.

- - Umbraco.tv → -
+
+
+

Easy start with Umbraco.tv

+

We have created a bunch of 'how-to' videos, to get you easily started with Umbraco. Learn how to build projects in just a couple of minutes. Easiest CMS in the world.

+ + Umbraco.tv → +
-
-

Be a part of the community

-

The Umbraco community is the best of its kind, be sure to visit, and if you have any questions, we're sure that you can get your answers from the community.

- - our.Umbraco → -
-
+
+

Be a part of the community

+

The Umbraco community is the best of its kind, be sure to visit, and if you have any questions, we’re sure that you can get your answers from the community.

+ + our.Umbraco → +
+
-
-
+
+
+
+ +<% }else{ %> + +
+
+ +
+ + +
+ +
+ +
+

Initializing...

+ Please wait while your site is loaded +
+ +

{{ vm.restore.restoreProgress }}% restored

+ {{ vm.restore.currentActivity }} + +
+

Initialize your site...

+ Press the button below to get started +
+ +
+
+
+ +
+

Restore from Umbraco Cloud

+ +
+ +
+

Restoring your website...

+

{{ vm.restore.restoreProgress }}% restored

+ {{ vm.restore.currentActivity }} +
+ +
+

Ready to rock n' roll!

+

Everything has been restored and is ready for use, click below to open Umbraco

+ +
+ + + + + <%--
+

An error occurred:

+

{{ vm.restore.error.exceptionMessage }}

+ Show details +
{{ vm.restore.error.log }}
+
--%> + +
+ +
+
- - +<%= NoNodesHelper.ServerVariables(HttpContext.Current.Request.RequestContext, UmbracoContext.Current) %> + + + + + + + + + +<% } %> - + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/trees.config b/src/Umbraco.Web.UI/config/trees.config index 13da9246b7..a6104af9ae 100644 --- a/src/Umbraco.Web.UI/config/trees.config +++ b/src/Umbraco.Web.UI/config/trees.config @@ -41,4 +41,5 @@ + \ No newline at end of file From f1149fb8269f4b875e5a2e763f172a0b225b5cde Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 13 Sep 2017 17:20:06 +0200 Subject: [PATCH 4/4] U4-10409 - revert unwanted files --- src/Umbraco.Web.UI/config/Dashboard.config | 10 -- .../config/splashes/noNodes.aspx | 158 ++++-------------- src/Umbraco.Web.UI/config/trees.config | 1 - 3 files changed, 32 insertions(+), 137 deletions(-) diff --git a/src/Umbraco.Web.UI/config/Dashboard.config b/src/Umbraco.Web.UI/config/Dashboard.config index c8c7e32f27..322551545d 100644 --- a/src/Umbraco.Web.UI/config/Dashboard.config +++ b/src/Umbraco.Web.UI/config/Dashboard.config @@ -119,14 +119,4 @@ -
- - content - - - - /App_Plugins/Deploy/views/dashboards/dashboard.html - - -
\ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/splashes/noNodes.aspx b/src/Umbraco.Web.UI/config/splashes/noNodes.aspx index 64d4446e3d..d46ecd6113 100644 --- a/src/Umbraco.Web.UI/config/splashes/noNodes.aspx +++ b/src/Umbraco.Web.UI/config/splashes/noNodes.aspx @@ -1,7 +1,7 @@ -<%@ Page Language="C#" AutoEventWireup="true"%> +<%@ Page Language="C#" AutoEventWireup="True" Inherits="Umbraco.Web.UI.Config.Splashes.NoNodes" CodeBehind="NoNodes.aspx.cs" %> +<%@ Import Namespace="Umbraco.Core.Configuration" %> <%@ Import Namespace="Umbraco.Core.IO" %> -<%@ Import Namespace="Umbraco.Deploy.UI" %> -<%@ Import Namespace="Umbraco.Web" %> + @@ -11,145 +11,51 @@ - + - - - - - - - + -<% if(HttpContext.Current.Request.IsLocal == false){ %>
-
-
- +
+
+ -

Welcome to your Umbraco installation

-

You're seeing the wonderful page because your website doesn't contain any published content yet.

+

Welcome to your Umbraco installation

+

You're seeing this wonderful page because your website doesn't contain any published content yet.

- + -
-
-

Easy start with Umbraco.tv

-

We have created a bunch of 'how-to' videos, to get you easily started with Umbraco. Learn how to build projects in just a couple of minutes. Easiest CMS in the world.

- - Umbraco.tv → -
+
+
+

Easy start with Umbraco.tv

+

We have created a bunch of 'how-to' videos, to get you easily started with Umbraco. Learn how to build projects in just a couple of minutes. Easiest CMS in the world.

+ + Umbraco.tv → +
-
-

Be a part of the community

-

The Umbraco community is the best of its kind, be sure to visit, and if you have any questions, we’re sure that you can get your answers from the community.

- - our.Umbraco → -
-
+
+

Be a part of the community

+

The Umbraco community is the best of its kind, be sure to visit, and if you have any questions, we're sure that you can get your answers from the community.

+ + our.Umbraco → +
+
-
-
-
- -<% }else{ %> - -
-
- -
- - -
- -
- -
-

Initializing...

- Please wait while your site is loaded -
- -

{{ vm.restore.restoreProgress }}% restored

- {{ vm.restore.currentActivity }} - -
-

Initialize your site...

- Press the button below to get started -
- -
-
-
- -
-

Restore from Umbraco Cloud

- -
- -
-

Restoring your website...

-

{{ vm.restore.restoreProgress }}% restored

- {{ vm.restore.currentActivity }} -
- -
-

Ready to rock n' roll!

-

Everything has been restored and is ready for use, click below to open Umbraco

- -
- - - - - <%--
-

An error occurred:

-

{{ vm.restore.error.exceptionMessage }}

- Show details -
{{ vm.restore.error.log }}
-
--%> - -
- -
-
+ +
-<%= NoNodesHelper.ServerVariables(HttpContext.Current.Request.RequestContext, UmbracoContext.Current) %> - - - - - - - - - -<% } %> + + - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/config/trees.config b/src/Umbraco.Web.UI/config/trees.config index a6104af9ae..13da9246b7 100644 --- a/src/Umbraco.Web.UI/config/trees.config +++ b/src/Umbraco.Web.UI/config/trees.config @@ -41,5 +41,4 @@ - \ No newline at end of file