diff --git a/src/Umbraco.Core/StringUdi.cs b/src/Umbraco.Core/StringUdi.cs
index 0f42f4b5f6..59eb40af7e 100644
--- a/src/Umbraco.Core/StringUdi.cs
+++ b/src/Umbraco.Core/StringUdi.cs
@@ -1,5 +1,6 @@
using System;
using System.ComponentModel;
+using System.Linq;
namespace Umbraco.Core
{
@@ -20,7 +21,7 @@ namespace Umbraco.Core
/// The entity type part of the udi.
/// The string id part of the udi.
public StringUdi(string entityType, string id)
- : base(entityType, "umb://" + entityType + "/" + Uri.EscapeUriString(id))
+ : base(entityType, "umb://" + entityType + "/" + EscapeUriString(id))
{
Id = id;
}
@@ -35,6 +36,19 @@ namespace Umbraco.Core
Id = Uri.UnescapeDataString(uriValue.AbsolutePath.TrimStart('/'));
}
+ private static string EscapeUriString(string s)
+ {
+ // Uri.EscapeUriString preserves / but also [ and ] which is bad
+ // Uri.EscapeDataString does not preserve / which is bad
+
+ // reserved = : / ? # [ ] @ ! $ & ' ( ) * + , ; =
+ // unreserved = alpha digit - . _ ~
+
+ // we want to preserve the / and the unreserved
+ // so...
+ return string.Join("/", s.Split('/').Select(Uri.EscapeDataString));
+ }
+
///
/// Converts the string representation of an entity identifier into the equivalent StringUdi instance.
///
diff --git a/src/Umbraco.Tests/App.config b/src/Umbraco.Tests/App.config
index 6500cd3741..84256a1e9b 100644
--- a/src/Umbraco.Tests/App.config
+++ b/src/Umbraco.Tests/App.config
@@ -79,6 +79,7 @@
+
diff --git a/src/Umbraco.Tests/UdiTests.cs b/src/Umbraco.Tests/UdiTests.cs
index 6e7e4671a9..f841f9e1a1 100644
--- a/src/Umbraco.Tests/UdiTests.cs
+++ b/src/Umbraco.Tests/UdiTests.cs
@@ -59,6 +59,29 @@ namespace Umbraco.Tests
Assert.AreEqual("umb://" + Constants.UdiEntityType.AnyString + "/path%20to/this%20is%20a%20test.xyz", udi3.ToString());
}
+ [Test]
+ public void StringEncodingTest2()
+ {
+ // reserved = : / ? # [ ] @ ! $ & ' ( ) * + , ; =
+ // unreserved = alpha digit - . _ ~
+
+ Assert.AreEqual("%3A%2F%3F%23%5B%5D%40%21%24%26%27%28%29%2B%2C%3B%3D.-_~%25", Uri.EscapeDataString(":/?#[]@!$&'()+,;=.-_~%"));
+ Assert.AreEqual(":/?#[]@!$&'()+,;=.-_~%25", Uri.EscapeUriString(":/?#[]@!$&'()+,;=.-_~%"));
+
+ // we cannot have reserved chars at random places
+ // we want to keep the / in string udis
+
+ var r = string.Join("/", "path/to/View[1].cshtml".Split('/').Select(Uri.EscapeDataString));
+ Assert.AreEqual("path/to/View%5B1%5D.cshtml", r);
+ Assert.IsTrue(Uri.IsWellFormedUriString("umb://partial-view-macro/" + r, UriKind.Absolute));
+
+ // 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");
+ Assert.AreEqual("path/to/View[1].cshtml", ((StringUdi) udi2).Id);
+ }
+
[Test]
public void GuidEntityCtorTest()
{
diff --git a/src/Umbraco.Tests/packages.config b/src/Umbraco.Tests/packages.config
index a9cbce1aad..4458fd06c5 100644
--- a/src/Umbraco.Tests/packages.config
+++ b/src/Umbraco.Tests/packages.config
@@ -21,6 +21,7 @@
+