diff --git a/src/Libraries/CoreNodeModels/DropDown.cs b/src/Libraries/CoreNodeModels/DropDown.cs index f528d7d345f..ef144c93520 100644 --- a/src/Libraries/CoreNodeModels/DropDown.cs +++ b/src/Libraries/CoreNodeModels/DropDown.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Security; using System.Xml; using CoreNodeModels.Properties; using Dynamo.Graph; @@ -164,7 +165,7 @@ protected override void DeserializeCore(XmlElement nodeElement, SaveContext cont } else { - selectedString = selectedIndex > Items.Count - 1? String.Empty: GetSelectedStringFromItem(Items.ElementAt(selectedIndex)); + selectedString = selectedIndex > Items.Count - 1 ? String.Empty : GetSelectedStringFromItem(Items.ElementAt(selectedIndex)); } } @@ -238,20 +239,30 @@ public static string SaveSelectedIndexImpl(int index, IList return string.Format("{0}:{1}", index, XmlEscape(item.Name)); } + /// + /// Escape string which could contain XML forbidden chars. + /// + /// + /// protected static string XmlEscape(string unescaped) { - var doc = new XmlDocument(); - XmlNode node = doc.CreateElement("root"); - node.InnerText = unescaped; - return node.InnerXml; + // TODO: This function can be simplified in Dynamo 3.0 + // since it is one line wrapper + return SecurityElement.Escape(unescaped); } + /// + /// Unescape string which could already be escaped before, + /// if there is no escaped special char, return as it is + /// if there is special char escaped, restore them. + /// + /// + /// protected static string XmlUnescape(string escaped) { - var doc = new XmlDocument(); - XmlNode node = doc.CreateElement("root"); - node.InnerXml = escaped; - return node.InnerText; + // TODO: This function can be simplified in Dynamo 3.0 + // since it is one line wrapper + return System.Web.HttpUtility.HtmlDecode(escaped); } /// diff --git a/test/DynamoCoreTests/Nodes/DropDownTests.cs b/test/DynamoCoreTests/Nodes/DropDownTests.cs index 9ef1b99bcc8..cfa6310ebb0 100644 --- a/test/DynamoCoreTests/Nodes/DropDownTests.cs +++ b/test/DynamoCoreTests/Nodes/DropDownTests.cs @@ -266,6 +266,16 @@ public void Save_SelectedIndex() DSDropDownBase.SaveSelectedIndexImpl(2, TestList())); } + [Test] + public void Save_SelectedIndexWithSpecialChar() + { + // This test makes sure that when SaveSelectedIndexImpl() is called with a index + // of item with Xml special char in the item.Name, the special char is escaped + Assert.AreEqual( + "2:<foo>", + DSDropDownBase.SaveSelectedIndexImpl(2, TestListWithItemWithSpecialChar())); + } + [Test] public void Load_NothingSelected() { @@ -296,6 +306,24 @@ public void Load_SelectionIndexNoNameMatch() Assert.AreEqual(-1, DSDropDownBase.ParseSelectedIndexImpl("2:foo", TestList())); } + [Test] + public void Load_SelectionIndexSpecialCharNoMatch() + { + Assert.AreEqual(-1, DSDropDownBase.ParseSelectedIndexImpl("2:", TestList())); + } + + [Test] + public void Load_SelectionIndexSpecialCharMatch() + { + Assert.AreEqual(2, DSDropDownBase.ParseSelectedIndexImpl("2:", TestListWithItemWithSpecialChar())); + } + + [Test] + public void Load_SelectionIndexSpecialCharMatch2() + { + Assert.AreEqual(2, DSDropDownBase.ParseSelectedIndexImpl("2:<foo>", TestListWithItemWithSpecialChar())); + } + private static List TestList() { var items = new List @@ -309,5 +337,17 @@ private static List TestList() return items; } + private static List TestListWithItemWithSpecialChar() + { + var items = new List + { + new DynamoDropDownItem("cat", "cat"), + new DynamoDropDownItem("dog", "dog"), + new DynamoDropDownItem("", ""), + new DynamoDropDownItem("!@#$%%%^&*()", "craziness") + }; + + return items; + } } }