diff --git a/src/Umbraco.Core/Dynamics/DynamicXml.cs b/src/Umbraco.Core/Dynamics/DynamicXml.cs index f0942d02a8..a3f9ae7089 100644 --- a/src/Umbraco.Core/Dynamics/DynamicXml.cs +++ b/src/Umbraco.Core/Dynamics/DynamicXml.cs @@ -9,35 +9,106 @@ using System.Xml.XPath; using System.Collections; using System.IO; using System.Web; +using Umbraco.Core; namespace Umbraco.Core.Dynamics { [TypeConverter(typeof(DynamicXmlConverter))] public class DynamicXml : DynamicObject, IEnumerable, IEnumerable { + /// + /// Returns the XElement used to create the DynamicXml structure + /// public XElement BaseElement { get; set; } + /// + /// Returns the raw XElement used to create the DynamicXml structure if one was specified otherwise returns the + /// same value as BaseElement. + /// + /// + /// This is purely used for when an instance of DynamicXml is created with the overload that supports + /// passing in both a raw xml version and a dash-stripped xml version. Otherwise this value is exactly the + /// same as BaseElement. + /// + public XElement RawXmlElement { get; internal set; } + + /// + /// Constructor + /// + /// public DynamicXml(XElement baseElement) { - this.BaseElement = baseElement; + if (baseElement == null) return; + + //same + RawXmlElement = baseElement; + BaseElement = baseElement; } - public DynamicXml(string xml) + + /// + /// When this constructor is used the BaseElement becomes equivalent to the strippedXml structure + /// + /// + /// + internal DynamicXml(XElement strippedXml, XElement rawXml) { - var baseElement = XElement.Parse(xml); - this.BaseElement = baseElement; + if (rawXml == null) return; + if (strippedXml == null) return; + + RawXmlElement = rawXml; + BaseElement = strippedXml; } + + /// + /// When this constructor is used the BaseElement becomes equivalent to the strippedXml structure + /// + /// + /// + internal DynamicXml(string strippedXml, string rawXml) + { + if (rawXml == null) return; + if (strippedXml == null) return; + + RawXmlElement = XElement.Parse(rawXml); + BaseElement = XElement.Parse(strippedXml); + } + + /// + /// Constructor + /// + /// + public DynamicXml(string xml) + { + if (xml.IsNullOrWhiteSpace()) return; + + var baseElement = XElement.Parse(xml); + + //same + RawXmlElement = baseElement; + BaseElement = baseElement; + } + + /// + /// Constructor + /// + /// public DynamicXml(XPathNodeIterator xpni) { - if (xpni != null) - { - if (xpni.Current != null) - { - var xml = xpni.Current.OuterXml; - var baseElement = XElement.Parse(xml); - this.BaseElement = baseElement; - } - } + if (xpni == null) return; + if (xpni.Current == null) return; + + //TODO: OuterXml is really bad for performance! Should actually use the XPathNodeIterator api + var xml = xpni.Current.OuterXml; + var baseElement = XElement.Parse(xml); + + //same + RawXmlElement = baseElement; + BaseElement = baseElement; } + + /// + /// Returns the InnertText based on the BaseElement object + /// public string InnerText { get @@ -45,17 +116,34 @@ namespace Umbraco.Core.Dynamics return BaseElement.Value; } } + + /// + /// Returns the string representation of the BaseElement object + /// + /// public string ToXml() { return BaseElement.ToString(SaveOptions.DisableFormatting); } + + /// + /// Returns the string representation of the RawXmlElement object + /// + /// + public string ToRawXml() + { + return RawXmlElement.ToString(SaveOptions.DisableFormatting); + } + public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { int index = 0; if (indexes.Length > 0) { index = (int)indexes[0]; - result = new DynamicXml(this.BaseElement.Elements().ToList()[index]); + result = new DynamicXml( + RawXmlElement.Elements().ElementAt(index), + BaseElement.Elements().ElementAt(index)); return true; } return base.TryGetIndex(binder, indexes, out result); @@ -64,12 +152,12 @@ namespace Umbraco.Core.Dynamics { if (args.Length == 0 && binder.Name == "ToXml") { - result = this.BaseElement.ToString(); + result = BaseElement.ToString(); return true; } if (args.Length == 1 && binder.Name == "XPath") { - var elements = this.BaseElement.XPathSelectElements(args[0].ToString()); + var elements = BaseElement.XPathSelectElements(args[0].ToString()); HandleIEnumerableXElement(elements, out result); return true; //anyway } @@ -102,7 +190,9 @@ namespace Umbraco.Core.Dynamics } else if (attempt.Result.ObjectResult is IEnumerable) { - result = ((IEnumerable)attempt.Result.ObjectResult).Select(x => new DynamicXml(x.BaseElement)); + result = ((IEnumerable) attempt.Result.ObjectResult).Select(x => new DynamicXml( + x.RawXmlElement, + x.BaseElement)); } } } @@ -123,55 +213,102 @@ namespace Umbraco.Core.Dynamics } public override bool TryGetMember(GetMemberBinder binder, out object result) { - if (BaseElement == null || binder == null) + if (RawXmlElement == null || binder == null) { result = null; return false; } - //Go ahead and try to fetch all of the elements matching the member name, and wrap them - var elements = BaseElement.Elements(binder.Name); - if (!elements.Any() && BaseElement.Name == "root" && BaseElement.Elements().Count() == 1) - { - //no elements matched, lets try first child - elements = BaseElement.Elements().ElementAt(0).Elements(binder.Name); - } - if (HandleIEnumerableXElement(elements, out result)) - { - return true; - } - else - { - //Ok, so no elements matched, so lets try attributes - IEnumerable attributes = BaseElement.Attributes(binder.Name).Select(attr => attr.Value); - int count = attributes.Count(); - - if (count > 0) + //Check if the name matches a node based on the BaseElement (which if the correct ctor is used, will be dash stripped) + var elementByNameAttempt = CheckNodeNameMatch(binder.Name, BaseElement); + if (elementByNameAttempt.Success) + { + if (HandleIEnumerableXElement(elementByNameAttempt.Result, out result)) { - if (count > 1) - result = attributes; //more than one attribute matched, lets return the collection - else - result = attributes.FirstOrDefault(); //only one attribute matched, lets just return it + return true; + } + } - return true; // return true because we matched + //Check if the name matches a node based on the BaseElement (which if the correct ctor is used, will be dash stripped) + var attributeByNameAttempt = CheckAttributeNameMatch(binder.Name, BaseElement); + if (attributeByNameAttempt.Success) + { + if (attributeByNameAttempt.Result.Count() > 1) + { + //more than one attribute matched, lets return the collection + result = attributeByNameAttempt.Result; } else { - //no attributes matched, lets try first child - if (BaseElement.Name == "root" && BaseElement.Elements().Count() == 1) - { - attributes = BaseElement.Elements().ElementAt(0).Attributes(binder.Name).Select(attr => attr.Value); - count = attributes.Count(); - if (count > 1) - result = attributes; //more than one attribute matched, lets return the collection - else - result = attributes.FirstOrDefault(); //only one attribute matched, lets just return it + //only one attribute matched, lets just return it + result = attributeByNameAttempt.Result.FirstOrDefault(); + } + return true; + } + - return true; // return true because we matched - } + return base.TryGetMember(binder, out result); + } + + /// + /// Checks if the 'name' matches any attributes of xmlElement + /// + /// The name to match + /// The xml element to check against + /// + private static Attempt> CheckAttributeNameMatch(string name, XElement xmlElement) + { + var attributes = xmlElement.Attributes(name).Select(attr => attr.Value).ToArray(); + if (attributes.Any()) + { + return new Attempt>(true, attributes); + } + + if (!attributes.Any() && xmlElement.Name == "root" && xmlElement.Elements().Count() == 1) + { + //no elements matched and this node is called 'root' and only has one child... lets see if it matches. + var childElements = xmlElement.Elements().ElementAt(0).Attributes(name).Select(attr => attr.Value).ToArray(); + if (childElements.Any()) + { + //we've found a match by the first child of an element called 'root' (strange, but sure) + return new Attempt>(true, childElements); + } + } + + //no deal + return Attempt>.False; + } + + /// + /// Checks if the 'name' matches any elements of xmlElement + /// + /// The name to match + /// The xml element to check against + /// + private Attempt> CheckNodeNameMatch(string name, XElement xmlElement) + { + //Go ahead and try to fetch all of the elements matching the member name, and wrap them + var elements = xmlElement.Elements(name).ToArray(); + + //Check if we've got any matches, if so then return true + if (elements.Any()) + { + return new Attempt>(true, elements); + } + + if (!elements.Any() && xmlElement.Name == "root" && xmlElement.Elements().Count() == 1) + { + //no elements matched and this node is called 'root' and only has one child... lets see if it matches. + var childElements = xmlElement.Elements().ElementAt(0).Elements(name).ToArray(); + if (childElements.Any()) + { + //we've found a match by the first child of an element called 'root' (strange, but sure) + return new Attempt>(true, childElements); } } - return base.TryGetMember(binder, out result); + + //no deal + return Attempt>.False; } private bool HandleIEnumerableXElement(IEnumerable elements, out object result) @@ -193,7 +330,7 @@ namespace Umbraco.Core.Dynamics //We have more than one matching element, so let's return the collection //elements is IEnumerable //but we want to be able to re-enter this code - XElement root = new XElement(XName.Get("root")); + var root = new XElement(XName.Get("root")); root.Add(elements); result = new DynamicXml(root); @@ -205,10 +342,16 @@ namespace Umbraco.Core.Dynamics result = null; return false; } + + /// + /// Executes an XPath expression over the BaseElement object + /// + /// + /// public DynamicXml XPath(string expression) { - var matched = this.BaseElement.XPathSelectElements(expression); - DynamicXml root = new DynamicXml(""); + var matched = BaseElement.XPathSelectElements(expression); + var root = new DynamicXml(""); foreach (var element in matched) { root.BaseElement.Add(element); @@ -217,7 +360,7 @@ namespace Umbraco.Core.Dynamics } /// - /// Return the string version of Xml + /// Return the string version of the BaseElement object /// /// public override string ToString() @@ -231,23 +374,23 @@ namespace Umbraco.Core.Dynamics } public DynamicXml Find(string expression) { - return new DynamicXml(this.BaseElement.XPathSelectElements(expression).FirstOrDefault()); + return new DynamicXml(BaseElement.XPathSelectElements(expression).FirstOrDefault()); } public DynamicXml Find(string attributeName, object value) { string expression = string.Format("//*[{0}='{1}']", attributeName, value); - return new DynamicXml(this.BaseElement.XPathSelectElements(expression).FirstOrDefault()); + return new DynamicXml(BaseElement.XPathSelectElements(expression).FirstOrDefault()); } IEnumerator IEnumerable.GetEnumerator() { - return this.BaseElement.Elements().GetEnumerator(); + return BaseElement.Elements().GetEnumerator(); } public IEnumerator GetEnumerator() { - return this.BaseElement.Elements().Select(e => new DynamicXml(e)).GetEnumerator(); + return BaseElement.Elements().Select(e => new DynamicXml(e)).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() @@ -310,7 +453,7 @@ namespace Umbraco.Core.Dynamics } public bool IsPosition(int index) { - if (this.BaseElement == null || this.BaseElement.Parent == null) + if (BaseElement == null || BaseElement.Parent == null) { return false; } @@ -318,7 +461,7 @@ namespace Umbraco.Core.Dynamics } public HtmlString IsPosition(int index, string valueIfTrue) { - if (this.BaseElement == null || this.BaseElement.Parent == null) + if (BaseElement == null || BaseElement.Parent == null) { return new HtmlString(string.Empty); } @@ -326,7 +469,7 @@ namespace Umbraco.Core.Dynamics } public HtmlString IsPosition(int index, string valueIfTrue, string valueIfFalse) { - if (this.BaseElement == null || this.BaseElement.Parent == null) + if (BaseElement == null || BaseElement.Parent == null) { return new HtmlString(valueIfFalse); } @@ -334,7 +477,7 @@ namespace Umbraco.Core.Dynamics } public bool IsModZero(int modulus) { - if (this.BaseElement == null || this.BaseElement.Parent == null) + if (BaseElement == null || BaseElement.Parent == null) { return false; } @@ -342,7 +485,7 @@ namespace Umbraco.Core.Dynamics } public HtmlString IsModZero(int modulus, string valueIfTrue) { - if (this.BaseElement == null || this.BaseElement.Parent == null) + if (BaseElement == null || BaseElement.Parent == null) { return new HtmlString(string.Empty); } @@ -350,7 +493,7 @@ namespace Umbraco.Core.Dynamics } public HtmlString IsModZero(int modulus, string valueIfTrue, string valueIfFalse) { - if (this.BaseElement == null || this.BaseElement.Parent == null) + if (BaseElement == null || BaseElement.Parent == null) { return new HtmlString(valueIfFalse); } @@ -359,7 +502,7 @@ namespace Umbraco.Core.Dynamics public bool IsNotModZero(int modulus) { - if (this.BaseElement == null || this.BaseElement.Parent == null) + if (BaseElement == null || BaseElement.Parent == null) { return false; } @@ -367,7 +510,7 @@ namespace Umbraco.Core.Dynamics } public HtmlString IsNotModZero(int modulus, string valueIfTrue) { - if (this.BaseElement == null || this.BaseElement.Parent == null) + if (BaseElement == null || this.BaseElement.Parent == null) { return new HtmlString(string.Empty); } @@ -573,7 +716,7 @@ namespace Umbraco.Core.Dynamics } public IEnumerable Descendants(Func func) { - var flattenedNodes = this.BaseElement.Elements().Map(func, (XElement n) => { return n.Elements(); }); + var flattenedNodes = this.BaseElement.Elements().Map(func, n => n.Elements()); return flattenedNodes.ToList().ConvertAll(n => new DynamicXml(n)); } public IEnumerable DescendantsOrSelf() @@ -582,7 +725,7 @@ namespace Umbraco.Core.Dynamics } public IEnumerable DescendantsOrSelf(Func func) { - var flattenedNodes = this.BaseElement.Elements().Map(func, (XElement n) => { return n.Elements(); }); + var flattenedNodes = this.BaseElement.Elements().Map(func, n => n.Elements()); var list = new List(); list.Add(this); list.AddRange(flattenedNodes.ToList().ConvertAll(n => new DynamicXml(n))); @@ -689,57 +832,10 @@ namespace Umbraco.Core.Dynamics return test(this) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); } + [Obsolete("Use XmlHelper.StripDashesInElementOrAttributeNames instead")] public static string StripDashesInElementOrAttributeNames(string xml) { - using (MemoryStream outputms = new MemoryStream()) - { - using (TextWriter outputtw = new StreamWriter(outputms)) - { - using (MemoryStream ms = new MemoryStream()) - { - using (TextWriter tw = new StreamWriter(ms)) - { - tw.Write(xml); - tw.Flush(); - ms.Position = 0; - using (TextReader tr = new StreamReader(ms)) - { - bool IsInsideElement = false, IsInsideQuotes = false; - int ic = 0; - while ((ic = tr.Read()) != -1) - { - if (ic == (int)'<' && !IsInsideQuotes) - { - if (tr.Peek() != (int)'!') - { - IsInsideElement = true; - } - } - if (ic == (int)'>' && !IsInsideQuotes) - { - IsInsideElement = false; - } - if (ic == (int)'"') - { - IsInsideQuotes = !IsInsideQuotes; - } - if (!IsInsideElement || ic != (int)'-' || IsInsideQuotes) - { - outputtw.Write((char)ic); - } - } - - } - } - } - outputtw.Flush(); - outputms.Position = 0; - using (TextReader outputtr = new StreamReader(outputms)) - { - return outputtr.ReadToEnd(); - } - } - } + return XmlHelper.StripDashesInElementOrAttributeNames(xml); } diff --git a/src/Umbraco.Core/Dynamics/DynamicXmlConverter.cs b/src/Umbraco.Core/Dynamics/DynamicXmlConverter.cs index 2c3f597cd3..f4ace38465 100644 --- a/src/Umbraco.Core/Dynamics/DynamicXmlConverter.cs +++ b/src/Umbraco.Core/Dynamics/DynamicXmlConverter.cs @@ -7,6 +7,58 @@ using System.Xml.Linq; namespace Umbraco.Core.Dynamics { + /// + /// Used to return the raw xml string value from DynamicXml when using type converters + /// + public class RawXmlString + { + public string Value { get; private set; } + + public RawXmlString(string value) + { + Value = value; + } + } + + /// + /// Used to return the raw xml XElement value from DynamicXml when using type converters + /// + public class RawXElement + { + public XElement Value { get; private set; } + + public RawXElement(XElement value) + { + Value = value; + } + } + + /// + /// Used to return the raw xml XElement value from DynamicXml when using type converters + /// + public class RawXmlElement + { + public XmlElement Value { get; private set; } + + public RawXmlElement(XmlElement value) + { + Value = value; + } + } + + /// + /// Used to return the raw xml XmlDocument value from DynamicXml when using type converters + /// + public class RawXmlDocument + { + public XmlDocument Value { get; private set; } + + public RawXmlDocument(XmlDocument value) + { + Value = value; + } + } + /// /// A custom type converter for DynamicXml /// @@ -14,7 +66,17 @@ namespace Umbraco.Core.Dynamics { public override bool CanConvertTo(ITypeDescriptorContext context, Type sourceType) { - var convertableTypes = new[] {typeof(string), typeof(XElement), typeof(XmlElement), typeof(XmlDocument)}; + var convertableTypes = new[] + { + typeof(string), + typeof(XElement), + typeof(XmlElement), + typeof(XmlDocument), + typeof(RawXmlString), + typeof(RawXElement), + typeof(RawXmlElement), + typeof(RawXmlDocument) + }; return convertableTypes.Any(x => TypeHelper.IsTypeAssignableFrom(x, sourceType)) || base.CanConvertFrom(context, sourceType); @@ -34,6 +96,7 @@ namespace Umbraco.Core.Dynamics { return value.ToString(); } + //XElement if (TypeHelper.IsTypeAssignableFrom(destinationType)) { @@ -53,6 +116,34 @@ namespace Umbraco.Core.Dynamics xDoc.LoadXml(dxml.ToString()); return xDoc; } + + //RAW values: + //string + if (TypeHelper.IsTypeAssignableFrom(destinationType)) + { + return new RawXmlString(dxml.ToRawXml()); + } + //XElement + if (TypeHelper.IsTypeAssignableFrom(destinationType)) + { + return new RawXElement(dxml.RawXmlElement); + } + //XmlElement + if (TypeHelper.IsTypeAssignableFrom(destinationType)) + { + var xDoc = new XmlDocument(); + xDoc.LoadXml(dxml.ToRawXml()); + return new RawXmlElement(xDoc.DocumentElement); + } + //XmlDocument + if (TypeHelper.IsTypeAssignableFrom(destinationType)) + { + var xDoc = new XmlDocument(); + xDoc.LoadXml(dxml.ToRawXml()); + return new RawXmlDocument(xDoc); + } + + return base.ConvertTo(context, culture, value, destinationType); } } diff --git a/src/Umbraco.Core/PublishedContentHelper.cs b/src/Umbraco.Core/PublishedContentHelper.cs index 94fc293e4c..d0710fa872 100644 --- a/src/Umbraco.Core/PublishedContentHelper.cs +++ b/src/Umbraco.Core/PublishedContentHelper.cs @@ -78,7 +78,7 @@ namespace Umbraco.Core { try { - var e = XElement.Parse(DynamicXml.StripDashesInElementOrAttributeNames(sResult), LoadOptions.None); + var e = XElement.Parse(sResult, LoadOptions.None); //check that the document element is not one of the disallowed elements //allows RTE to still return as html if it's valid xhtml diff --git a/src/Umbraco.Core/XmlHelper.cs b/src/Umbraco.Core/XmlHelper.cs index 27f4d0e923..3e67f2728e 100644 --- a/src/Umbraco.Core/XmlHelper.cs +++ b/src/Umbraco.Core/XmlHelper.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Xml; @@ -14,6 +15,59 @@ namespace Umbraco.Core public class XmlHelper { + public static string StripDashesInElementOrAttributeNames(string xml) + { + using (var outputms = new MemoryStream()) + { + using (TextWriter outputtw = new StreamWriter(outputms)) + { + using (var ms = new MemoryStream()) + { + using (var tw = new StreamWriter(ms)) + { + tw.Write(xml); + tw.Flush(); + ms.Position = 0; + using (var tr = new StreamReader(ms)) + { + bool IsInsideElement = false, IsInsideQuotes = false; + int ic = 0; + while ((ic = tr.Read()) != -1) + { + if (ic == (int)'<' && !IsInsideQuotes) + { + if (tr.Peek() != (int)'!') + { + IsInsideElement = true; + } + } + if (ic == (int)'>' && !IsInsideQuotes) + { + IsInsideElement = false; + } + if (ic == (int)'"') + { + IsInsideQuotes = !IsInsideQuotes; + } + if (!IsInsideElement || ic != (int)'-' || IsInsideQuotes) + { + outputtw.Write((char)ic); + } + } + + } + } + } + outputtw.Flush(); + outputms.Position = 0; + using (TextReader outputtr = new StreamReader(outputms)) + { + return outputtr.ReadToEnd(); + } + } + } + } + /// /// Imports a XML node from text. /// diff --git a/src/Umbraco.Tests/PublishedContent/DynamicXmlConverterTests.cs b/src/Umbraco.Tests/PublishedContent/DynamicXmlConverterTests.cs new file mode 100644 index 0000000000..f0ad32a1bc --- /dev/null +++ b/src/Umbraco.Tests/PublishedContent/DynamicXmlConverterTests.cs @@ -0,0 +1,110 @@ +using System.Xml; +using System.Xml.Linq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Dynamics; +using Umbraco.Tests.PartialTrust; + +namespace Umbraco.Tests.PublishedContent +{ + [TestFixture] + public class DynamicXmlConverterTests : AbstractPartialTrustFixture + { + [Test] + public void Convert_To_Raw_String() + { + var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; + var dXml = new DynamicXml( + XmlHelper.StripDashesInElementOrAttributeNames(xml), + xml); + var result = dXml.TryConvertTo(); + Assert.IsTrue(result.Success); + Assert.AreEqual(xml, result.Result.Value); + } + + [Test] + public void Convert_To_Raw_XElement() + { + var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; + var dXml = new DynamicXml( + XmlHelper.StripDashesInElementOrAttributeNames(xml), + xml); + var result = dXml.TryConvertTo(); + Assert.IsTrue(result.Success); + Assert.AreEqual(xml, result.Result.Value.ToString(SaveOptions.DisableFormatting)); + } + + [Test] + public void Convert_To_Raw_XmlElement() + { + var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; + var dXml = new DynamicXml( + XmlHelper.StripDashesInElementOrAttributeNames(xml), + xml); + var result = dXml.TryConvertTo(); + Assert.IsTrue(result.Success); + Assert.AreEqual(xml, result.Result.Value.OuterXml); + } + + [Test] + public void Convert_To_Raw_XmlDocument() + { + var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; + var dXml = new DynamicXml( + XmlHelper.StripDashesInElementOrAttributeNames(xml), + xml); + var result = dXml.TryConvertTo(); + Assert.IsTrue(result.Success); + Assert.AreEqual(xml, result.Result.Value.InnerXml); + } + + [Test] + public void Convert_To_String() + { + var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; + var dXml = new DynamicXml(xml); + var result = dXml.TryConvertTo(); + Assert.IsTrue(result.Success); + Assert.AreEqual(xml, result.Result); + } + + [Test] + public void Convert_To_XElement() + { + var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; + var dXml = new DynamicXml(xml); + var result = dXml.TryConvertTo(); + Assert.IsTrue(result.Success); + Assert.AreEqual(xml, result.Result.ToString(SaveOptions.DisableFormatting)); + } + + [Test] + public void Convert_To_XmlElement() + { + var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; + var dXml = new DynamicXml(xml); + var result = dXml.TryConvertTo(); + Assert.IsTrue(result.Success); + Assert.AreEqual(xml, result.Result.OuterXml); + } + + [Test] + public void Convert_To_XmlDocument() + { + var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; + var dXml = new DynamicXml(xml); + var result = dXml.TryConvertTo(); + Assert.IsTrue(result.Success); + Assert.AreEqual(xml, result.Result.InnerXml); + } + + public override void TestSetup() + { + + } + + public override void TestTearDown() + { + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/PublishedContent/DynamicXmlTests.cs b/src/Umbraco.Tests/PublishedContent/DynamicXmlTests.cs index 4fc304730a..693ecf02ef 100644 --- a/src/Umbraco.Tests/PublishedContent/DynamicXmlTests.cs +++ b/src/Umbraco.Tests/PublishedContent/DynamicXmlTests.cs @@ -1,74 +1,50 @@ using System; using System.Diagnostics; -using System.Xml; -using System.Xml.Linq; using Microsoft.CSharp.RuntimeBinder; using NUnit.Framework; +using Umbraco.Core; using Umbraco.Core.Dynamics; using System.Linq; -using Umbraco.Tests.PartialTrust; -using Umbraco.Core; namespace Umbraco.Tests.PublishedContent { - [TestFixture] - public class DynamicXmlConverterTests : AbstractPartialTrustFixture - { - [Test] - public void Convert_To_String() - { - var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; - var dXml = new DynamicXml(xml); - var result = dXml.TryConvertTo(); - Assert.IsTrue(result.Success); - Assert.AreEqual(xml, result.Result); - } - - [Test] - public void Convert_To_XElement() - { - var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; - var dXml = new DynamicXml(xml); - var result = dXml.TryConvertTo(); - Assert.IsTrue(result.Success); - Assert.AreEqual(xml, result.Result.ToString(SaveOptions.DisableFormatting)); - } - - [Test] - public void Convert_To_XmlElement() - { - var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; - var dXml = new DynamicXml(xml); - var result = dXml.TryConvertTo(); - Assert.IsTrue(result.Success); - Assert.AreEqual(xml, result.Result.OuterXml); - } - - [Test] - public void Convert_To_XmlDocument() - { - var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; - var dXml = new DynamicXml(xml); - var result = dXml.TryConvertTo(); - Assert.IsTrue(result.Success); - Assert.AreEqual(xml, result.Result.InnerXml); - } - - public override void TestSetup() - { - - } - - public override void TestTearDown() - { - } - } - - [TestFixture] + [TestFixture] public class DynamicXmlTests { + /// + /// Ensures that when we return the xml structure we get the real structure, not the replaced hyphen structure + /// see: http://issues.umbraco.org/issue/U4-1405#comment=67-5113 + /// http://issues.umbraco.org/issue/U4-1636 + /// + [Test] + public void Deals_With_Hyphenated_values() + { + var xml = @" + + True + 1161 + /content/ + 12 december Zorgbeurs Care + +"; - [Test] + var typedXml = new DynamicXml( + XmlHelper.StripDashesInElementOrAttributeNames(xml), + xml); + dynamic dynamicXml = typedXml; + + var typedElement = typedXml.RawXmlElement.Element("url-picker"); + var dynamicElementByCleanedName = dynamicXml.urlpicker; + + Assert.IsNotNull(typedElement); + Assert.IsNotNull(dynamicElementByCleanedName); + + Assert.AreEqual( + typedElement.Attribute("some-attribute").Value, + dynamicElementByCleanedName.someattribute); + } + + [Test] public void Custom_Extension_Method_Legacy() { var xml = "/media/54/tulips.jpg1024768620888jpg/media/41/hydrangeas.jpg1024768595284jpg"; @@ -92,7 +68,7 @@ namespace Umbraco.Tests.PublishedContent //we haven't explicitly defined ElementAt so this will dynamically invoke this method var element = dynamicXml.ElementAt(0); - Assert.AreEqual("1057", Enumerable.First(element.BaseElement.Elements()).Attribute("id").Value); + Assert.AreEqual("1057", Enumerable.First(element.BaseElement.Elements()).Attribute("id").Value); } [Test] @@ -123,8 +99,8 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(1, typedTaken.Count()); Assert.AreEqual(1, Enumerable.Count(dynamicTaken)); - Assert.AreEqual("1057", typedTaken.ElementAt(0).BaseElement.Elements().First().Attribute("id").Value); - Assert.AreEqual("1057", Enumerable.First(Enumerable.ElementAt(dynamicTaken, 0).BaseElement.Elements()).Attribute("id").Value); + Assert.AreEqual("1057", typedTaken.ElementAt(0).BaseElement.Elements().First().Attribute("id").Value); + Assert.AreEqual("1057", Enumerable.First(Enumerable.ElementAt(dynamicTaken, 0).BaseElement.Elements()).Attribute("id").Value); } [Test] diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 2b631b8cbf..164713a5f1 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -187,6 +187,7 @@ + diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/LegacyConverter.cs b/src/umbraco.MacroEngines/RazorDynamicNode/LegacyConverter.cs index 209c246003..6a68cf1a82 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/LegacyConverter.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/LegacyConverter.cs @@ -12,7 +12,7 @@ namespace umbraco.MacroEngines { if (result is Umbraco.Core.Dynamics.DynamicXml) { - result = new DynamicXml(((Umbraco.Core.Dynamics.DynamicXml)result).BaseElement); + result = new DynamicXml(((Umbraco.Core.Dynamics.DynamicXml)result).BaseElement); } else if (result is Umbraco.Core.Dynamics.DynamicNull) {