From fce8ac87a2649917d7101c2fa622bf5296160f3b Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 12 Nov 2019 12:37:21 +1100 Subject: [PATCH 01/13] Moves a bunch of the xml/xpath classes/interfaces and remvoes unused code of XmlHelper --- .../Xml/XPath/INavigableContent.cs | 2 +- .../Xml/XPath/INavigableContentType.cs | 2 +- .../Xml/XPath/INavigableFieldType.cs | 2 +- .../Xml/XPath/INavigableSource.cs | 2 +- .../Xml/XPath/MacroNavigator.cs | 4 +- .../Xml/XPath/NavigableNavigator.cs | 8 +- .../Xml/XPath/RenamedRootNavigator.cs | 0 .../Xml/XPathVariable.cs | 0 src/Umbraco.Abstractions/Xml/XmlHelper.cs | 221 +++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 10 +- src/Umbraco.Core/Xml/XmlHelper.cs | 430 ------------------ 11 files changed, 232 insertions(+), 449 deletions(-) rename src/{Umbraco.Core => Umbraco.Abstractions}/Xml/XPath/INavigableContent.cs (98%) rename src/{Umbraco.Core => Umbraco.Abstractions}/Xml/XPath/INavigableContentType.cs (93%) rename src/{Umbraco.Core => Umbraco.Abstractions}/Xml/XPath/INavigableFieldType.cs (95%) rename src/{Umbraco.Core => Umbraco.Abstractions}/Xml/XPath/INavigableSource.cs (96%) rename src/{Umbraco.Core => Umbraco.Abstractions}/Xml/XPath/MacroNavigator.cs (99%) rename src/{Umbraco.Core => Umbraco.Abstractions}/Xml/XPath/NavigableNavigator.cs (99%) rename src/{Umbraco.Core => Umbraco.Abstractions}/Xml/XPath/RenamedRootNavigator.cs (100%) rename src/{Umbraco.Core => Umbraco.Abstractions}/Xml/XPathVariable.cs (100%) create mode 100644 src/Umbraco.Abstractions/Xml/XmlHelper.cs delete mode 100644 src/Umbraco.Core/Xml/XmlHelper.cs diff --git a/src/Umbraco.Core/Xml/XPath/INavigableContent.cs b/src/Umbraco.Abstractions/Xml/XPath/INavigableContent.cs similarity index 98% rename from src/Umbraco.Core/Xml/XPath/INavigableContent.cs rename to src/Umbraco.Abstractions/Xml/XPath/INavigableContent.cs index eeb7891726..c0b3192830 100644 --- a/src/Umbraco.Core/Xml/XPath/INavigableContent.cs +++ b/src/Umbraco.Abstractions/Xml/XPath/INavigableContent.cs @@ -5,7 +5,7 @@ namespace Umbraco.Core.Xml.XPath /// /// Represents a content that can be navigated via XPath. /// - interface INavigableContent + public interface INavigableContent { /// /// Gets the unique identifier of the navigable content. diff --git a/src/Umbraco.Core/Xml/XPath/INavigableContentType.cs b/src/Umbraco.Abstractions/Xml/XPath/INavigableContentType.cs similarity index 93% rename from src/Umbraco.Core/Xml/XPath/INavigableContentType.cs rename to src/Umbraco.Abstractions/Xml/XPath/INavigableContentType.cs index 94b225467c..1fff0b60bb 100644 --- a/src/Umbraco.Core/Xml/XPath/INavigableContentType.cs +++ b/src/Umbraco.Abstractions/Xml/XPath/INavigableContentType.cs @@ -8,7 +8,7 @@ namespace Umbraco.Core.Xml.XPath /// /// Represents the type of a content that can be navigated via XPath. /// - interface INavigableContentType + public interface INavigableContentType { /// /// Gets the name of the content type. diff --git a/src/Umbraco.Core/Xml/XPath/INavigableFieldType.cs b/src/Umbraco.Abstractions/Xml/XPath/INavigableFieldType.cs similarity index 95% rename from src/Umbraco.Core/Xml/XPath/INavigableFieldType.cs rename to src/Umbraco.Abstractions/Xml/XPath/INavigableFieldType.cs index 32a6f64751..d741060865 100644 --- a/src/Umbraco.Core/Xml/XPath/INavigableFieldType.cs +++ b/src/Umbraco.Abstractions/Xml/XPath/INavigableFieldType.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core.Xml.XPath /// Represents the type of a field of a content that can be navigated via XPath. /// /// A field can be an attribute or a property. - interface INavigableFieldType + public interface INavigableFieldType { /// /// Gets the name of the field type. diff --git a/src/Umbraco.Core/Xml/XPath/INavigableSource.cs b/src/Umbraco.Abstractions/Xml/XPath/INavigableSource.cs similarity index 96% rename from src/Umbraco.Core/Xml/XPath/INavigableSource.cs rename to src/Umbraco.Abstractions/Xml/XPath/INavigableSource.cs index 68dc9e906a..7a605574d7 100644 --- a/src/Umbraco.Core/Xml/XPath/INavigableSource.cs +++ b/src/Umbraco.Abstractions/Xml/XPath/INavigableSource.cs @@ -8,7 +8,7 @@ namespace Umbraco.Core.Xml.XPath /// /// Represents a source of content that can be navigated via XPath. /// - interface INavigableSource + public interface INavigableSource { /// /// Gets a content identified by its unique identifier. diff --git a/src/Umbraco.Core/Xml/XPath/MacroNavigator.cs b/src/Umbraco.Abstractions/Xml/XPath/MacroNavigator.cs similarity index 99% rename from src/Umbraco.Core/Xml/XPath/MacroNavigator.cs rename to src/Umbraco.Abstractions/Xml/XPath/MacroNavigator.cs index 8b4755107a..dc2246834e 100644 --- a/src/Umbraco.Core/Xml/XPath/MacroNavigator.cs +++ b/src/Umbraco.Abstractions/Xml/XPath/MacroNavigator.cs @@ -10,7 +10,7 @@ namespace Umbraco.Core.Xml.XPath /// /// Provides a cursor model for navigating {macro /} as if it were XML. /// - class MacroNavigator : XPathNavigator + public class MacroNavigator : XPathNavigator { private readonly XmlNameTable _nameTable; private readonly MacroRoot _macro; @@ -55,7 +55,7 @@ namespace Umbraco.Core.Xml.XPath private int _tabs; private readonly int _uid = GetUid(); private static int _uidg; - private readonly static object Uidl = new object(); + private static readonly object Uidl = new object(); private static int GetUid() { lock (Uidl) diff --git a/src/Umbraco.Core/Xml/XPath/NavigableNavigator.cs b/src/Umbraco.Abstractions/Xml/XPath/NavigableNavigator.cs similarity index 99% rename from src/Umbraco.Core/Xml/XPath/NavigableNavigator.cs rename to src/Umbraco.Abstractions/Xml/XPath/NavigableNavigator.cs index dc89de8372..e178ab918e 100644 --- a/src/Umbraco.Core/Xml/XPath/NavigableNavigator.cs +++ b/src/Umbraco.Abstractions/Xml/XPath/NavigableNavigator.cs @@ -25,7 +25,7 @@ namespace Umbraco.Core.Xml.XPath /// /// Provides a cursor model for navigating Umbraco data as if it were XML. /// - class NavigableNavigator : XPathNavigator + public class NavigableNavigator : XPathNavigator { // "The XmlNameTable stores atomized strings of any local name, namespace URI, // and prefix used by the XPathNavigator. This means that when the same Name is @@ -1089,7 +1089,7 @@ namespace Umbraco.Core.Xml.XPath #region State management // the possible state positions - internal enum StatePosition + public enum StatePosition { Root, Element, @@ -1101,10 +1101,10 @@ namespace Umbraco.Core.Xml.XPath // gets the state // for unit tests only - internal State InternalState => _state; + public State InternalState => _state; // represents the XPathNavigator state - internal class State + public class State { public StatePosition Position { get; set; } diff --git a/src/Umbraco.Core/Xml/XPath/RenamedRootNavigator.cs b/src/Umbraco.Abstractions/Xml/XPath/RenamedRootNavigator.cs similarity index 100% rename from src/Umbraco.Core/Xml/XPath/RenamedRootNavigator.cs rename to src/Umbraco.Abstractions/Xml/XPath/RenamedRootNavigator.cs diff --git a/src/Umbraco.Core/Xml/XPathVariable.cs b/src/Umbraco.Abstractions/Xml/XPathVariable.cs similarity index 100% rename from src/Umbraco.Core/Xml/XPathVariable.cs rename to src/Umbraco.Abstractions/Xml/XPathVariable.cs diff --git a/src/Umbraco.Abstractions/Xml/XmlHelper.cs b/src/Umbraco.Abstractions/Xml/XmlHelper.cs new file mode 100644 index 0000000000..0a435b23f7 --- /dev/null +++ b/src/Umbraco.Abstractions/Xml/XmlHelper.cs @@ -0,0 +1,221 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.XPath; +using Umbraco.Core.Exceptions; + +namespace Umbraco.Core.Xml +{ + /// + /// The XmlHelper class contains general helper methods for working with xml in umbraco. + /// + public class XmlHelper + { + + /// + /// Gets a value indicating whether a specified string contains only xml whitespace characters. + /// + /// The string. + /// true if the string contains only xml whitespace characters. + /// As per XML 1.1 specs, space, \t, \r and \n. + public static bool IsXmlWhitespace(string s) + { + // as per xml 1.1 specs - anything else is significant whitespace + s = s.Trim(' ', '\t', '\r', '\n'); + return s.Length == 0; + } + + /// + /// Creates a new XPathDocument from an xml string. + /// + /// The xml string. + /// An XPathDocument created from the xml string. + public static XPathDocument CreateXPathDocument(string xml) + { + return new XPathDocument(new XmlTextReader(new StringReader(xml))); + } + + /// + /// Tries to create a new XPathDocument from an xml string. + /// + /// The xml string. + /// The XPath document. + /// A value indicating whether it has been possible to create the document. + public static bool TryCreateXPathDocument(string xml, out XPathDocument doc) + { + try + { + doc = CreateXPathDocument(xml); + return true; + } + catch (Exception) + { + doc = null; + return false; + } + } + + /// + /// Tries to create a new XPathDocument from a property value. + /// + /// The value of the property. + /// The XPath document. + /// A value indicating whether it has been possible to create the document. + /// The value can be anything... Performance-wise, this is bad. + public static bool TryCreateXPathDocumentFromPropertyValue(object value, out XPathDocument doc) + { + // DynamicNode.ConvertPropertyValueByDataType first cleans the value by calling + // XmlHelper.StripDashesInElementOrAttributeName - this is because the XML is + // to be returned as a DynamicXml and element names such as "value-item" are + // invalid and must be converted to "valueitem". But we don't have that sort of + // problem here - and we don't need to bother with dashes nor dots, etc. + + doc = null; + var xml = value as string; + if (xml == null) return false; // no a string + if (CouldItBeXml(xml) == false) return false; // string does not look like it's xml + if (IsXmlWhitespace(xml)) return false; // string is whitespace, xml-wise + if (TryCreateXPathDocument(xml, out doc) == false) return false; // string can't be parsed into xml + + var nav = doc.CreateNavigator(); + if (nav.MoveToFirstChild()) + { + //SD: This used to do this but the razor macros and the entire razor macros section is gone, it was all legacy, it seems this method isn't even + // used apart from for tests so don't think this matters. In any case, we no longer check for this! + + //var name = nav.LocalName; // must not match an excluded tag + //if (UmbracoConfig.For.UmbracoSettings().Scripting.NotDynamicXmlDocumentElements.All(x => x.Element.InvariantEquals(name) == false)) return true; + + return true; + } + + doc = null; + return false; + } + + + /// + /// Sorts the children of a parentNode. + /// + /// The parent node. + /// An XPath expression to select children of to sort. + /// A function returning the value to order the nodes by. + public static void SortNodes( + XmlNode parentNode, + string childNodesXPath, + Func orderBy) + { + var sortedChildNodes = parentNode.SelectNodes(childNodesXPath).Cast() + .OrderBy(orderBy) + .ToArray(); + + // append child nodes to last position, in sort-order + // so all child nodes will go after the property nodes + foreach (var node in sortedChildNodes) + parentNode.AppendChild(node); // moves the node to the last position + } + + + /// + /// Sorts a single child node of a parentNode. + /// + /// The parent node. + /// An XPath expression to select children of to sort. + /// The child node to sort. + /// A function returning the value to order the nodes by. + /// A value indicating whether sorting was needed. + /// Assuming all nodes but are sorted, this will move the node to + /// the right position without moving all the nodes (as SortNodes would do) - should improve perfs. + public static bool SortNode( + XmlNode parentNode, + string childNodesXPath, + XmlNode node, + Func orderBy) + { + var nodeSortOrder = orderBy(node); + var childNodesAndOrder = parentNode.SelectNodes(childNodesXPath).Cast() + .Select(x => Tuple.Create(x, orderBy(x))).ToArray(); + + // only one node = node is in the right place already, obviously + if (childNodesAndOrder.Length == 1) return false; + + // find the first node with a sortOrder > node.sortOrder + var i = 0; + while (i < childNodesAndOrder.Length && childNodesAndOrder[i].Item2 <= nodeSortOrder) + i++; + + // if one was found + if (i < childNodesAndOrder.Length) + { + // and node is just before, we're done already + // else we need to move it right before the node that was found + if (i == 0 || childNodesAndOrder[i - 1].Item1 != node) + { + parentNode.InsertBefore(node, childNodesAndOrder[i].Item1); + return true; + } + } + else // i == childNodesAndOrder.Length && childNodesAndOrder.Length > 1 + { + // and node is the last one, we're done already + // else we need to append it as the last one + // (and i > 1, see above) + if (childNodesAndOrder[i - 1].Item1 != node) + { + parentNode.AppendChild(node); + return true; + } + } + return false; + } + + + + /// + /// Gets the value of a XmlNode + /// + /// The XmlNode. + /// the value as a string + public static string GetNodeValue(XmlNode n) + { + var value = string.Empty; + if (n == null || n.FirstChild == null) + return value; + value = n.FirstChild.Value ?? n.InnerXml; + return value.Replace("", "", "]]>"); + } + + /// + /// Determines whether the specified string appears to be XML. + /// + /// The XML string. + /// + /// true if the specified string appears to be XML; otherwise, false. + /// + public static bool CouldItBeXml(string xml) + { + if (string.IsNullOrEmpty(xml)) return false; + + xml = xml.Trim(); + return xml.StartsWith("<") && xml.EndsWith(">") && xml.Contains('/'); + } + + /// + /// Return a dictionary of attributes found for a string based tag + /// + /// + /// + public static Dictionary GetAttributesFromElement(string tag) + { + var m = + Regex.Matches(tag, "(?\\S*)=\"(?[^\"]*)\"", + RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); + // fix for issue 14862: return lowercase attributes for case insensitive matching + var d = m.Cast().ToDictionary(attributeSet => attributeSet.Groups["attributeName"].Value.ToString().ToLower(), attributeSet => attributeSet.Groups["attributeValue"].Value.ToString()); + return d; + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 5f18cb3d95..34c2295a9f 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -1169,18 +1169,9 @@ - - - - - - - - - @@ -1194,5 +1185,6 @@ Umbraco.Abstractions + \ No newline at end of file diff --git a/src/Umbraco.Core/Xml/XmlHelper.cs b/src/Umbraco.Core/Xml/XmlHelper.cs deleted file mode 100644 index 53d56c1aac..0000000000 --- a/src/Umbraco.Core/Xml/XmlHelper.cs +++ /dev/null @@ -1,430 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using System.Xml; -using System.Xml.Linq; -using System.Xml.XPath; -using Umbraco.Core.Composing; -using Umbraco.Core.Exceptions; -using Umbraco.Core.IO; - -namespace Umbraco.Core.Xml -{ - /// - /// The XmlHelper class contains general helper methods for working with xml in umbraco. - /// - public class XmlHelper - { - /// - /// Creates or sets an attribute on the XmlNode if an Attributes collection is available - /// - /// - /// - /// - /// - public static void SetAttribute(XmlDocument xml, XmlNode n, string name, string value) - { - if (xml == null) throw new ArgumentNullException("xml"); - if (n == null) throw new ArgumentNullException("n"); - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); - - if (n.Attributes == null) - { - return; - } - if (n.Attributes[name] == null) - { - var a = xml.CreateAttribute(name); - a.Value = value; - n.Attributes.Append(a); - } - else - { - n.Attributes[name].Value = value; - } - } - - /// - /// Gets a value indicating whether a specified string contains only xml whitespace characters. - /// - /// The string. - /// true if the string contains only xml whitespace characters. - /// As per XML 1.1 specs, space, \t, \r and \n. - public static bool IsXmlWhitespace(string s) - { - // as per xml 1.1 specs - anything else is significant whitespace - s = s.Trim(' ', '\t', '\r', '\n'); - return s.Length == 0; - } - - /// - /// Creates a new XPathDocument from an xml string. - /// - /// The xml string. - /// An XPathDocument created from the xml string. - public static XPathDocument CreateXPathDocument(string xml) - { - return new XPathDocument(new XmlTextReader(new StringReader(xml))); - } - - /// - /// Tries to create a new XPathDocument from an xml string. - /// - /// The xml string. - /// The XPath document. - /// A value indicating whether it has been possible to create the document. - public static bool TryCreateXPathDocument(string xml, out XPathDocument doc) - { - try - { - doc = CreateXPathDocument(xml); - return true; - } - catch (Exception) - { - doc = null; - return false; - } - } - - /// - /// Tries to create a new XPathDocument from a property value. - /// - /// The value of the property. - /// The XPath document. - /// A value indicating whether it has been possible to create the document. - /// The value can be anything... Performance-wise, this is bad. - public static bool TryCreateXPathDocumentFromPropertyValue(object value, out XPathDocument doc) - { - // DynamicNode.ConvertPropertyValueByDataType first cleans the value by calling - // XmlHelper.StripDashesInElementOrAttributeName - this is because the XML is - // to be returned as a DynamicXml and element names such as "value-item" are - // invalid and must be converted to "valueitem". But we don't have that sort of - // problem here - and we don't need to bother with dashes nor dots, etc. - - doc = null; - var xml = value as string; - if (xml == null) return false; // no a string - if (CouldItBeXml(xml) == false) return false; // string does not look like it's xml - if (IsXmlWhitespace(xml)) return false; // string is whitespace, xml-wise - if (TryCreateXPathDocument(xml, out doc) == false) return false; // string can't be parsed into xml - - var nav = doc.CreateNavigator(); - if (nav.MoveToFirstChild()) - { - //SD: This used to do this but the razor macros and the entire razor macros section is gone, it was all legacy, it seems this method isn't even - // used apart from for tests so don't think this matters. In any case, we no longer check for this! - - //var name = nav.LocalName; // must not match an excluded tag - //if (UmbracoConfig.For.UmbracoSettings().Scripting.NotDynamicXmlDocumentElements.All(x => x.Element.InvariantEquals(name) == false)) return true; - - return true; - } - - doc = null; - return false; - } - - - /// - /// Sorts the children of a parentNode. - /// - /// The parent node. - /// An XPath expression to select children of to sort. - /// A function returning the value to order the nodes by. - internal static void SortNodes( - XmlNode parentNode, - string childNodesXPath, - Func orderBy) - { - var sortedChildNodes = parentNode.SelectNodes(childNodesXPath).Cast() - .OrderBy(orderBy) - .ToArray(); - - // append child nodes to last position, in sort-order - // so all child nodes will go after the property nodes - foreach (var node in sortedChildNodes) - parentNode.AppendChild(node); // moves the node to the last position - } - - - /// - /// Sorts a single child node of a parentNode. - /// - /// The parent node. - /// An XPath expression to select children of to sort. - /// The child node to sort. - /// A function returning the value to order the nodes by. - /// A value indicating whether sorting was needed. - /// Assuming all nodes but are sorted, this will move the node to - /// the right position without moving all the nodes (as SortNodes would do) - should improve perfs. - internal static bool SortNode( - XmlNode parentNode, - string childNodesXPath, - XmlNode node, - Func orderBy) - { - var nodeSortOrder = orderBy(node); - var childNodesAndOrder = parentNode.SelectNodes(childNodesXPath).Cast() - .Select(x => Tuple.Create(x, orderBy(x))).ToArray(); - - // only one node = node is in the right place already, obviously - if (childNodesAndOrder.Length == 1) return false; - - // find the first node with a sortOrder > node.sortOrder - var i = 0; - while (i < childNodesAndOrder.Length && childNodesAndOrder[i].Item2 <= nodeSortOrder) - i++; - - // if one was found - if (i < childNodesAndOrder.Length) - { - // and node is just before, we're done already - // else we need to move it right before the node that was found - if (i == 0 || childNodesAndOrder[i - 1].Item1 != node) - { - parentNode.InsertBefore(node, childNodesAndOrder[i].Item1); - return true; - } - } - else // i == childNodesAndOrder.Length && childNodesAndOrder.Length > 1 - { - // and node is the last one, we're done already - // else we need to append it as the last one - // (and i > 1, see above) - if (childNodesAndOrder[i - 1].Item1 != node) - { - parentNode.AppendChild(node); - return true; - } - } - return false; - } - - - /// - /// Opens a file as a XmlDocument. - /// - /// The relative file path. ie. /config/umbraco.config - /// Returns a XmlDocument class - public static XmlDocument OpenAsXmlDocument(string filePath) - { - using (var reader = new XmlTextReader(Current.IOHelper.MapPath(filePath)) {WhitespaceHandling = WhitespaceHandling.All}) - { - var xmlDoc = new XmlDocument(); - //Load the file into the XmlDocument - xmlDoc.Load(reader); - - return xmlDoc; - } - } - - /// - /// creates a XmlAttribute with the specified name and value - /// - /// The xmldocument. - /// The name of the attribute. - /// The value of the attribute. - /// a XmlAttribute - public static XmlAttribute AddAttribute(XmlDocument xd, string name, string value) - { - if (xd == null) throw new ArgumentNullException("xd"); - if (string.IsNullOrEmpty(name)) throw new ArgumentNullOrEmptyException(nameof(name)); - - var temp = xd.CreateAttribute(name); - temp.Value = value; - return temp; - } - - /// - /// Creates a text XmlNode with the specified name and value - /// - /// The xmldocument. - /// The node name. - /// The node value. - /// a XmlNode - public static XmlNode AddTextNode(XmlDocument xd, string name, string value) - { - if (xd == null) throw new ArgumentNullException("xd"); - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); - - var temp = xd.CreateNode(XmlNodeType.Element, name, ""); - temp.AppendChild(xd.CreateTextNode(value)); - return temp; - } - - /// - /// Sets or Creates a text XmlNode with the specified name and value - /// - /// The xmldocument. - /// The node to set or create the child text node on - /// The node name. - /// The node value. - /// a XmlNode - public static XmlNode SetTextNode(XmlDocument xd, XmlNode parent, string name, string value) - { - if (xd == null) throw new ArgumentNullException("xd"); - if (parent == null) throw new ArgumentNullException("parent"); - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); - - var child = parent.SelectSingleNode(name); - if (child != null) - { - child.InnerText = value; - return child; - } - return AddTextNode(xd, name, value); - } - - /// - /// Sets or creates an Xml node from its inner Xml. - /// - /// The xmldocument. - /// The node to set or create the child text node on - /// The node name. - /// The node inner Xml. - /// a XmlNode - public static XmlNode SetInnerXmlNode(XmlDocument xd, XmlNode parent, string name, string value) - { - if (xd == null) throw new ArgumentNullException(nameof(xd)); - if (parent == null) throw new ArgumentNullException(nameof(parent)); - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(name)); - - var child = parent.SelectSingleNode(name) ?? xd.CreateNode(XmlNodeType.Element, name, ""); - child.InnerXml = value; - return child; - } - - /// - /// Creates a cdata XmlNode with the specified name and value - /// - /// The xmldocument. - /// The node name. - /// The node value. - /// A XmlNode - public static XmlNode AddCDataNode(XmlDocument xd, string name, string value) - { - if (xd == null) throw new ArgumentNullException("xd"); - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); - - var temp = xd.CreateNode(XmlNodeType.Element, name, ""); - temp.AppendChild(xd.CreateCDataSection(value)); - return temp; - } - - /// - /// Sets or Creates a cdata XmlNode with the specified name and value - /// - /// The xmldocument. - /// The node to set or create the child text node on - /// The node name. - /// The node value. - /// a XmlNode - public static XmlNode SetCDataNode(XmlDocument xd, XmlNode parent, string name, string value) - { - if (xd == null) throw new ArgumentNullException("xd"); - if (parent == null) throw new ArgumentNullException("parent"); - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); - - var child = parent.SelectSingleNode(name); - if (child != null) - { - child.InnerXml = ""; ; - return child; - } - return AddCDataNode(xd, name, value); - } - - /// - /// Gets the value of a XmlNode - /// - /// The XmlNode. - /// the value as a string - internal static string GetNodeValue(XmlNode n) - { - var value = string.Empty; - if (n == null || n.FirstChild == null) - return value; - value = n.FirstChild.Value ?? n.InnerXml; - return value.Replace("", "", "]]>"); - } - - /// - /// Determines whether the specified string appears to be XML. - /// - /// The XML string. - /// - /// true if the specified string appears to be XML; otherwise, false. - /// - public static bool CouldItBeXml(string xml) - { - if (string.IsNullOrEmpty(xml)) return false; - - xml = xml.Trim(); - return xml.StartsWith("<") && xml.EndsWith(">") && xml.Contains('/'); - } - - /// - /// Splits the specified delimited string into an XML document. - /// - /// The data. - /// The separator. - /// Name of the root. - /// Name of the element. - /// Returns an System.Xml.XmlDocument representation of the delimited string data. - internal static XmlDocument Split(string data, string[] separator, string rootName, string elementName) - { - return Split(new XmlDocument(), data, separator, rootName, elementName); - } - - /// - /// Splits the specified delimited string into an XML document. - /// - /// The XML document. - /// The delimited string data. - /// The separator. - /// Name of the root node. - /// Name of the element node. - /// Returns an System.Xml.XmlDocument representation of the delimited string data. - internal static XmlDocument Split(XmlDocument xml, string data, string[] separator, string rootName, string elementName) - { - // load new XML document. - xml.LoadXml(string.Concat("<", rootName, "/>")); - - // get the data-value, check it isn't empty. - if (!string.IsNullOrEmpty(data)) - { - // explode the values into an array - var values = data.Split(separator, StringSplitOptions.None); - - // loop through the array items. - foreach (string value in values) - { - // add each value to the XML document. - var xn = XmlHelper.AddTextNode(xml, elementName, value); - xml.DocumentElement.AppendChild(xn); - } - } - - // return the XML node. - return xml; - } - - /// - /// Return a dictionary of attributes found for a string based tag - /// - /// - /// - internal static Dictionary GetAttributesFromElement(string tag) - { - var m = - Regex.Matches(tag, "(?\\S*)=\"(?[^\"]*)\"", - RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - // fix for issue 14862: return lowercase attributes for case insensitive matching - var d = m.Cast().ToDictionary(attributeSet => attributeSet.Groups["attributeName"].Value.ToString().ToLower(), attributeSet => attributeSet.Groups["attributeValue"].Value.ToString()); - return d; - } - } -} From e58edd7d5d18968c09ccd1e79ee683b236f4cd38 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 12 Nov 2019 12:56:17 +1100 Subject: [PATCH 02/13] Moves remaining Umbraco.Core.Xml items to abstractions project --- .../Xml/DynamicContext.cs | 0 .../Xml/UmbracoXPathPathSyntaxParser.cs | 4 +--- .../Xml/XPathNavigatorExtensions.cs | 2 +- .../Xml/XmlNamespaces.cs | 0 .../Xml/XmlNodeListFactory.cs | 2 +- src/{Umbraco.Core => Umbraco.Abstractions}/XmlExtensions.cs | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 6 ------ 7 files changed, 4 insertions(+), 12 deletions(-) rename src/{Umbraco.Core => Umbraco.Abstractions}/Xml/DynamicContext.cs (100%) rename src/{Umbraco.Core => Umbraco.Abstractions}/Xml/UmbracoXPathPathSyntaxParser.cs (98%) rename src/{Umbraco.Core => Umbraco.Abstractions}/Xml/XPathNavigatorExtensions.cs (98%) rename src/{Umbraco.Core => Umbraco.Abstractions}/Xml/XmlNamespaces.cs (100%) rename src/{Umbraco.Core => Umbraco.Abstractions}/Xml/XmlNodeListFactory.cs (99%) rename src/{Umbraco.Core => Umbraco.Abstractions}/XmlExtensions.cs (99%) diff --git a/src/Umbraco.Core/Xml/DynamicContext.cs b/src/Umbraco.Abstractions/Xml/DynamicContext.cs similarity index 100% rename from src/Umbraco.Core/Xml/DynamicContext.cs rename to src/Umbraco.Abstractions/Xml/DynamicContext.cs diff --git a/src/Umbraco.Core/Xml/UmbracoXPathPathSyntaxParser.cs b/src/Umbraco.Abstractions/Xml/UmbracoXPathPathSyntaxParser.cs similarity index 98% rename from src/Umbraco.Core/Xml/UmbracoXPathPathSyntaxParser.cs rename to src/Umbraco.Abstractions/Xml/UmbracoXPathPathSyntaxParser.cs index b31fa6a8df..0c8136421d 100644 --- a/src/Umbraco.Core/Xml/UmbracoXPathPathSyntaxParser.cs +++ b/src/Umbraco.Abstractions/Xml/UmbracoXPathPathSyntaxParser.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Umbraco.Core.Exceptions; namespace Umbraco.Core.Xml @@ -11,7 +9,7 @@ namespace Umbraco.Core.Xml /// This is used to parse our customize Umbraco XPath expressions (i.e. that include special tokens like $site) into /// a real XPath statement /// - internal class UmbracoXPathPathSyntaxParser + public class UmbracoXPathPathSyntaxParser { /// /// Parses custom umbraco xpath expression diff --git a/src/Umbraco.Core/Xml/XPathNavigatorExtensions.cs b/src/Umbraco.Abstractions/Xml/XPathNavigatorExtensions.cs similarity index 98% rename from src/Umbraco.Core/Xml/XPathNavigatorExtensions.cs rename to src/Umbraco.Abstractions/Xml/XPathNavigatorExtensions.cs index b80836dde8..bb3b41511b 100644 --- a/src/Umbraco.Core/Xml/XPathNavigatorExtensions.cs +++ b/src/Umbraco.Abstractions/Xml/XPathNavigatorExtensions.cs @@ -5,7 +5,7 @@ namespace Umbraco.Core.Xml /// /// Provides extensions to XPathNavigator. /// - internal static class XPathNavigatorExtensions + public static class XPathNavigatorExtensions { /// /// Selects a node set, using the specified XPath expression. diff --git a/src/Umbraco.Core/Xml/XmlNamespaces.cs b/src/Umbraco.Abstractions/Xml/XmlNamespaces.cs similarity index 100% rename from src/Umbraco.Core/Xml/XmlNamespaces.cs rename to src/Umbraco.Abstractions/Xml/XmlNamespaces.cs diff --git a/src/Umbraco.Core/Xml/XmlNodeListFactory.cs b/src/Umbraco.Abstractions/Xml/XmlNodeListFactory.cs similarity index 99% rename from src/Umbraco.Core/Xml/XmlNodeListFactory.cs rename to src/Umbraco.Abstractions/Xml/XmlNodeListFactory.cs index bc8deaab2a..0a5f2c859d 100644 --- a/src/Umbraco.Core/Xml/XmlNodeListFactory.cs +++ b/src/Umbraco.Abstractions/Xml/XmlNodeListFactory.cs @@ -7,7 +7,7 @@ using System.Xml.XPath; namespace Umbraco.Core.Xml { - class XmlNodeListFactory + public class XmlNodeListFactory { private XmlNodeListFactory() { } diff --git a/src/Umbraco.Core/XmlExtensions.cs b/src/Umbraco.Abstractions/XmlExtensions.cs similarity index 99% rename from src/Umbraco.Core/XmlExtensions.cs rename to src/Umbraco.Abstractions/XmlExtensions.cs index 199e16195b..ef0132dd69 100644 --- a/src/Umbraco.Core/XmlExtensions.cs +++ b/src/Umbraco.Abstractions/XmlExtensions.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core /// /// Extension methods for xml objects /// - internal static class XmlExtensions + public static class XmlExtensions { public static bool HasAttribute(this XmlAttributeCollection attributes, string attributeName) { diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 34c2295a9f..51187a49b7 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -1166,12 +1166,6 @@ Properties\SolutionInfo.cs - - - - - - From 38986f3bfd66f3936170c67508e7252adeae5aaf Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 12 Nov 2019 13:33:02 +1100 Subject: [PATCH 03/13] Abstracts out IStylesheet, IStylesheetProperty --- .../Models/IStylesheet.cs | 30 +++++++++++++++++ .../Models/IStylesheetProperty.cs | 11 +++++++ src/Umbraco.Abstractions/Models/Stylesheet.cs | 6 ++-- .../Models/StylesheetProperty.cs | 2 +- .../Repositories/IStylesheetRepository.cs | 4 +-- .../Implement/StylesheetRepository.cs | 33 +++++++++++-------- src/Umbraco.Core/Services/IFileService.cs | 8 ++--- .../Services/Implement/FileService.cs | 24 +++++++------- .../Repositories/StylesheetRepositoryTest.cs | 32 ++++++++++-------- src/Umbraco.Tests/Testing/UmbracoTestBase.cs | 4 +++ .../Cache/DistributedCacheBinder_Handlers.cs | 4 +-- src/Umbraco.Web/Editors/CodeFileController.cs | 6 ++-- 12 files changed, 111 insertions(+), 53 deletions(-) create mode 100644 src/Umbraco.Abstractions/Models/IStylesheet.cs create mode 100644 src/Umbraco.Abstractions/Models/IStylesheetProperty.cs diff --git a/src/Umbraco.Abstractions/Models/IStylesheet.cs b/src/Umbraco.Abstractions/Models/IStylesheet.cs new file mode 100644 index 0000000000..737118d646 --- /dev/null +++ b/src/Umbraco.Abstractions/Models/IStylesheet.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using Umbraco.Core.Models.Entities; + +namespace Umbraco.Core.Models +{ + public interface IStylesheet : IFile, IRememberBeingDirty + { + /// + /// Returns a list of umbraco back office enabled stylesheet properties + /// + /// + /// An umbraco back office enabled stylesheet property has a special prefix, for example: + /// + /// /** umb_name: MyPropertyName */ p { font-size: 1em; } + /// + IEnumerable Properties { get; } + + /// + /// Adds an Umbraco stylesheet property for use in the back office + /// + /// + void AddProperty(IStylesheetProperty property); + + /// + /// Removes an Umbraco stylesheet property + /// + /// + void RemoveProperty(string name); + } +} diff --git a/src/Umbraco.Abstractions/Models/IStylesheetProperty.cs b/src/Umbraco.Abstractions/Models/IStylesheetProperty.cs new file mode 100644 index 0000000000..c44a147d54 --- /dev/null +++ b/src/Umbraco.Abstractions/Models/IStylesheetProperty.cs @@ -0,0 +1,11 @@ +using Umbraco.Core.Models.Entities; + +namespace Umbraco.Core.Models +{ + public interface IStylesheetProperty : IRememberBeingDirty + { + string Alias { get; set; } + string Name { get; } + string Value { get; set; } + } +} diff --git a/src/Umbraco.Abstractions/Models/Stylesheet.cs b/src/Umbraco.Abstractions/Models/Stylesheet.cs index df5786a340..48f00a1650 100644 --- a/src/Umbraco.Abstractions/Models/Stylesheet.cs +++ b/src/Umbraco.Abstractions/Models/Stylesheet.cs @@ -13,7 +13,7 @@ namespace Umbraco.Core.Models /// [Serializable] [DataContract(IsReference = true)] - public class Stylesheet : File + public class Stylesheet : File, IStylesheet { public Stylesheet(string path) : this(path, null) @@ -120,7 +120,7 @@ namespace Umbraco.Core.Models /// /** umb_name: MyPropertyName */ p { font-size: 1em; } /// [IgnoreDataMember] - public IEnumerable Properties + public IEnumerable Properties { get { return _properties.Value; } } @@ -129,7 +129,7 @@ namespace Umbraco.Core.Models /// Adds an Umbraco stylesheet property for use in the back office /// /// - public void AddProperty(StylesheetProperty property) + public void AddProperty(IStylesheetProperty property) { if (Properties.Any(x => x.Name.InvariantEquals(property.Name))) { diff --git a/src/Umbraco.Abstractions/Models/StylesheetProperty.cs b/src/Umbraco.Abstractions/Models/StylesheetProperty.cs index 089f89deb6..bc895113bc 100644 --- a/src/Umbraco.Abstractions/Models/StylesheetProperty.cs +++ b/src/Umbraco.Abstractions/Models/StylesheetProperty.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Models /// [Serializable] [DataContract(IsReference = true)] - public class StylesheetProperty : BeingDirtyBase, IValueObject + public class StylesheetProperty : BeingDirtyBase, IValueObject, IStylesheetProperty { private string _alias; private string _value; diff --git a/src/Umbraco.Core/Persistence/Repositories/IStylesheetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IStylesheetRepository.cs index 1643e6e7a7..10771eb6d0 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IStylesheetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IStylesheetRepository.cs @@ -3,9 +3,9 @@ using Umbraco.Core.Models; namespace Umbraco.Core.Persistence.Repositories { - public interface IStylesheetRepository : IReadRepository, IWriteRepository + public interface IStylesheetRepository : IReadRepository, IWriteRepository { - bool ValidateStylesheet(Stylesheet stylesheet); + bool ValidateStylesheet(IStylesheet stylesheet); Stream GetFileContentStream(string filepath); void SetFileContent(string filepath, Stream content); long GetFileSize(string filepath); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/StylesheetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/StylesheetRepository.cs index 52ff14b0dc..d5db3e2cf4 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/StylesheetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/StylesheetRepository.cs @@ -10,15 +10,19 @@ namespace Umbraco.Core.Persistence.Repositories.Implement /// /// Represents the Stylesheet Repository /// - internal class StylesheetRepository : FileRepository, IStylesheetRepository + internal class StylesheetRepository : FileRepository, IStylesheetRepository { - public StylesheetRepository(IFileSystems fileSystems) + private readonly IIOHelper _ioHelper; + + public StylesheetRepository(IFileSystems fileSystems, IIOHelper ioHelper) : base(fileSystems.StylesheetsFileSystem) - { } + { + _ioHelper = ioHelper; + } #region Overrides of FileRepository - public override Stylesheet Get(string id) + public override IStylesheet Get(string id) { // get the relative path within the filesystem // (though... id should be relative already) @@ -51,16 +55,19 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } - public override void Save(Stylesheet entity) + public override void Save(IStylesheet entity) { - base.Save(entity); + // TODO: Casting :/ Do we need GetFileContent below? Need to look into it later + var stylesheet = (Stylesheet)entity; + + base.Save(stylesheet); // ensure that from now on, content is lazy-loaded - if (entity.GetFileContent == null) - entity.GetFileContent = file => GetFileContent(file.OriginalPath); + if (stylesheet.GetFileContent == null) + stylesheet.GetFileContent = file => GetFileContent(file.OriginalPath); } - public override IEnumerable GetMany(params string[] ids) + public override IEnumerable GetMany(params string[] ids) { //ensure they are de-duplicated, easy win if people don't do this as this can cause many excess queries ids = ids @@ -92,14 +99,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement /// If null or not specified, will return the stylesheets at the root path relative to the IFileSystem /// /// - public IEnumerable GetStylesheetsAtPath(string rootPath = null) + public IEnumerable GetStylesheetsAtPath(string rootPath = null) { return FileSystem.GetFiles(rootPath ?? string.Empty, "*.css").Select(Get); } private static readonly List ValidExtensions = new List { "css" }; - public bool ValidateStylesheet(Stylesheet stylesheet) + public bool ValidateStylesheet(IStylesheet stylesheet) { // get full path string fullPath; @@ -115,8 +122,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // validate path and extension var validDir = SystemDirectories.Css; - var isValidPath = Current.IOHelper.VerifyEditPath(fullPath, validDir); - var isValidExtension = Current.IOHelper.VerifyFileExtension(stylesheet.Path, ValidExtensions); + var isValidPath = _ioHelper.VerifyEditPath(fullPath, validDir); + var isValidExtension = _ioHelper.VerifyFileExtension(stylesheet.Path, ValidExtensions); return isValidPath && isValidExtension; } diff --git a/src/Umbraco.Core/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs index 5fe52559ee..eba7b98ef2 100644 --- a/src/Umbraco.Core/Services/IFileService.cs +++ b/src/Umbraco.Core/Services/IFileService.cs @@ -31,21 +31,21 @@ namespace Umbraco.Core.Services /// Gets a list of all objects /// /// An enumerable list of objects - IEnumerable GetStylesheets(params string[] names); + IEnumerable GetStylesheets(params string[] names); /// /// Gets a object by its name /// /// Name of the stylesheet incl. extension /// A object - Stylesheet GetStylesheetByName(string name); + IStylesheet GetStylesheetByName(string name); /// /// Saves a /// /// to save /// Optional id of the user saving the stylesheet - void SaveStylesheet(Stylesheet stylesheet, int userId = Constants.Security.SuperUserId); + void SaveStylesheet(IStylesheet stylesheet, int userId = Constants.Security.SuperUserId); /// /// Deletes a stylesheet by its name @@ -59,7 +59,7 @@ namespace Umbraco.Core.Services /// /// to validate /// True if Stylesheet is valid, otherwise false - bool ValidateStylesheet(Stylesheet stylesheet); + bool ValidateStylesheet(IStylesheet stylesheet); /// /// Gets a list of all objects diff --git a/src/Umbraco.Core/Services/Implement/FileService.cs b/src/Umbraco.Core/Services/Implement/FileService.cs index 2cdff9a8e4..a6615ca574 100644 --- a/src/Umbraco.Core/Services/Implement/FileService.cs +++ b/src/Umbraco.Core/Services/Implement/FileService.cs @@ -49,7 +49,7 @@ namespace Umbraco.Core.Services.Implement /// Gets a list of all objects /// /// An enumerable list of objects - public IEnumerable GetStylesheets(params string[] names) + public IEnumerable GetStylesheets(params string[] names) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { @@ -62,7 +62,7 @@ namespace Umbraco.Core.Services.Implement /// /// Name of the stylesheet incl. extension /// A object - public Stylesheet GetStylesheetByName(string name) + public IStylesheet GetStylesheetByName(string name) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { @@ -75,11 +75,11 @@ namespace Umbraco.Core.Services.Implement /// /// to save /// - public void SaveStylesheet(Stylesheet stylesheet, int userId = Constants.Security.SuperUserId) + public void SaveStylesheet(IStylesheet stylesheet, int userId = Constants.Security.SuperUserId) { using (var scope = ScopeProvider.CreateScope()) { - var saveEventArgs = new SaveEventArgs(stylesheet); + var saveEventArgs = new SaveEventArgs(stylesheet); if (scope.Events.DispatchCancelable(SavingStylesheet, this, saveEventArgs)) { scope.Complete(); @@ -91,7 +91,7 @@ namespace Umbraco.Core.Services.Implement saveEventArgs.CanCancel = false; scope.Events.Dispatch(SavedStylesheet, this, saveEventArgs); - Audit(AuditType.Save, userId, -1, ObjectTypes.GetName(UmbracoObjectTypes.Stylesheet)); + Audit(AuditType.Save, userId, -1, UmbracoObjectTypes.Stylesheet.GetName()); scope.Complete(); } } @@ -112,7 +112,7 @@ namespace Umbraco.Core.Services.Implement return; } - var deleteEventArgs = new DeleteEventArgs(stylesheet); + var deleteEventArgs = new DeleteEventArgs(stylesheet); if (scope.Events.DispatchCancelable(DeletingStylesheet, this, deleteEventArgs)) { scope.Complete(); @@ -123,7 +123,7 @@ namespace Umbraco.Core.Services.Implement deleteEventArgs.CanCancel = false; scope.Events.Dispatch(DeletedStylesheet, this, deleteEventArgs); - Audit(AuditType.Delete, userId, -1, ObjectTypes.GetName(UmbracoObjectTypes.Stylesheet)); + Audit(AuditType.Delete, userId, -1, UmbracoObjectTypes.Stylesheet.GetName()); scope.Complete(); } } @@ -133,7 +133,7 @@ namespace Umbraco.Core.Services.Implement /// /// to validate /// True if Stylesheet is valid, otherwise false - public bool ValidateStylesheet(Stylesheet stylesheet) + public bool ValidateStylesheet(IStylesheet stylesheet) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { @@ -1116,12 +1116,12 @@ namespace Umbraco.Core.Services.Implement /// /// Occurs before Delete /// - public static event TypedEventHandler> DeletingStylesheet; + public static event TypedEventHandler> DeletingStylesheet; /// /// Occurs after Delete /// - public static event TypedEventHandler> DeletedStylesheet; + public static event TypedEventHandler> DeletedStylesheet; /// /// Occurs before Save @@ -1146,12 +1146,12 @@ namespace Umbraco.Core.Services.Implement /// /// Occurs before Save /// - public static event TypedEventHandler> SavingStylesheet; + public static event TypedEventHandler> SavingStylesheet; /// /// Occurs after Save /// - public static event TypedEventHandler> SavedStylesheet; + public static event TypedEventHandler> SavedStylesheet; /// /// Occurs before Save diff --git a/src/Umbraco.Tests/Persistence/Repositories/StylesheetRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/StylesheetRepositoryTest.cs index 6fae1d4749..2358fb257d 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/StylesheetRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/StylesheetRepositoryTest.cs @@ -6,6 +6,7 @@ using Moq; using NUnit.Framework; using Umbraco.Core.IO; using Umbraco.Core.Models; +using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; @@ -30,6 +31,11 @@ namespace Umbraco.Tests.Persistence.Repositories _fileSystem.AddFile("styles.css", stream); } + private IStylesheetRepository CreateRepository() + { + return new StylesheetRepository(_fileSystems, new IOHelper()); + } + [Test] public void Can_Instantiate_Repository() { @@ -37,7 +43,7 @@ namespace Umbraco.Tests.Persistence.Repositories using (ScopeProvider.CreateScope()) { // Act - var repository = new StylesheetRepository(_fileSystems); + var repository = CreateRepository(); // Assert @@ -51,7 +57,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystems); + var repository = CreateRepository(); // Act var stylesheet = new Stylesheet("test-add.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; @@ -69,7 +75,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystems); + var repository = CreateRepository(); // Act var stylesheet = new Stylesheet("test-update.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; @@ -96,10 +102,10 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystems); + var repository = CreateRepository(); // Act - var stylesheet = new Stylesheet("test-update.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; + IStylesheet stylesheet = new Stylesheet("test-update.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; repository.Save(stylesheet); @@ -123,7 +129,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystems); + var repository = CreateRepository(); // Act var stylesheet = new Stylesheet("test-update.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; @@ -142,7 +148,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystems); + var repository = CreateRepository(); // Act var stylesheet = new Stylesheet("test-delete.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; @@ -163,7 +169,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystems); + var repository = CreateRepository(); // Act var stylesheet = repository.Get("styles.css"); @@ -182,7 +188,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystems); + var repository = CreateRepository(); var stylesheet = new Stylesheet("styles-v2.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; repository.Save(stylesheet); @@ -205,7 +211,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystems); + var repository = CreateRepository(); var stylesheet = new Stylesheet("styles-v2.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; repository.Save(stylesheet); @@ -228,7 +234,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Arrange using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystems); + var repository = CreateRepository(); // Act var exists = repository.Exists("styles.css"); @@ -245,9 +251,9 @@ namespace Umbraco.Tests.Persistence.Repositories using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystems); + var repository = CreateRepository(); - var stylesheet = new Stylesheet("test-path-1.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; + IStylesheet stylesheet = new Stylesheet("test-path-1.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; repository.Save(stylesheet); Assert.IsTrue(_fileSystem.FileExists("test-path-1.css")); diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index b9fd0f6640..5b0e486a52 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -99,6 +99,8 @@ namespace Umbraco.Tests.Testing protected ILogger Logger => Factory.GetInstance(); + protected IIOHelper IOHelper { get; private set; } + protected IProfiler Profiler => Factory.GetInstance(); protected virtual IProfilingLogger ProfilingLogger => Factory.GetInstance(); @@ -129,6 +131,7 @@ namespace Umbraco.Tests.Testing var (logger, profiler) = GetLoggers(Options.Logger); var proflogger = new ProfilingLogger(logger, profiler); + IOHelper = new IOHelper(); var appCaches = GetAppCaches(); var globalSettings = SettingsForTests.GetDefaultGlobalSettings(); var typeLoader = GetTypeLoader(appCaches.RuntimeCache, globalSettings, proflogger, Options.TypeLoader); @@ -137,6 +140,7 @@ namespace Umbraco.Tests.Testing Composition = new Composition(register, typeLoader, proflogger, ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + Composition.RegisterUnique(IOHelper); Composition.RegisterUnique(typeLoader); Composition.RegisterUnique(logger); Composition.RegisterUnique(profiler); diff --git a/src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs b/src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs index 3b2cf3e23d..c56b7a094f 100644 --- a/src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs +++ b/src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs @@ -380,8 +380,8 @@ namespace Umbraco.Web.Cache } // TODO: our weird events handling wants this for now - private void FileService_DeletedStylesheet(IFileService sender, DeleteEventArgs e) { } - private void FileService_SavedStylesheet(IFileService sender, SaveEventArgs e) { } + private void FileService_DeletedStylesheet(IFileService sender, DeleteEventArgs e) { } + private void FileService_SavedStylesheet(IFileService sender, SaveEventArgs e) { } #endregion diff --git a/src/Umbraco.Web/Editors/CodeFileController.cs b/src/Umbraco.Web/Editors/CodeFileController.cs index ae870f695e..c5ded5cada 100644 --- a/src/Umbraco.Web/Editors/CodeFileController.cs +++ b/src/Umbraco.Web/Editors/CodeFileController.cs @@ -190,7 +190,7 @@ namespace Umbraco.Web.Editors var stylesheet = Services.FileService.GetStylesheetByName(virtualPath); if (stylesheet != null) { - var display = Mapper.Map(stylesheet); + var display = Mapper.Map(stylesheet); display.FileType = Core.Constants.Trees.Stylesheets; display.Path = Url.GetTreePathFromFilePath(stylesheet.Path); display.Id = System.Web.HttpUtility.UrlEncode(stylesheet.Path); @@ -511,7 +511,7 @@ namespace Umbraco.Web.Editors name => new Script(name)); } - private Stylesheet CreateOrUpdateStylesheet(CodeFileDisplay display) + private IStylesheet CreateOrUpdateStylesheet(CodeFileDisplay display) { return CreateOrUpdateFile(display, ".css", Current.FileSystems.StylesheetsFileSystem, name => Services.FileService.GetStylesheetByName(name), @@ -521,7 +521,7 @@ namespace Umbraco.Web.Editors } private T CreateOrUpdateFile(CodeFileDisplay display, string extension, IFileSystem fileSystem, - Func getFileByName, Action saveFile, Func createFile) where T : Core.Models.File + Func getFileByName, Action saveFile, Func createFile) where T : Core.Models.IFile { //must always end with the correct extension display.Name = EnsureCorrectFileExtension(display.Name, extension); From 545a427d1165da1b49b22b7134a14140719ffa13 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 12 Nov 2019 13:49:56 +1100 Subject: [PATCH 04/13] abstracts out IScript, stops casting stuff --- src/Umbraco.Abstractions/Models/IScript.cs | 7 ++ src/Umbraco.Abstractions/Models/Script.cs | 7 +- .../Repositories/IScriptRepository.cs | 7 +- .../Repositories/IStylesheetRepository.cs | 3 + .../Implement/ScriptRepository.cs | 29 +++--- .../Implement/StylesheetRepository.cs | 2 +- src/Umbraco.Core/Services/IFileService.cs | 44 ++++----- .../Services/Implement/FileService.cs | 90 ++++++------------- .../Repositories/ScriptRepositoryTest.cs | 30 ++++--- src/Umbraco.Web/Editors/CodeFileController.cs | 4 +- .../Models/Mapping/CodeFileMapDefinition.cs | 16 ++-- 11 files changed, 110 insertions(+), 129 deletions(-) create mode 100644 src/Umbraco.Abstractions/Models/IScript.cs diff --git a/src/Umbraco.Abstractions/Models/IScript.cs b/src/Umbraco.Abstractions/Models/IScript.cs new file mode 100644 index 0000000000..9fdc321107 --- /dev/null +++ b/src/Umbraco.Abstractions/Models/IScript.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core.Models +{ + public interface IScript : IFile + { + + } +} diff --git a/src/Umbraco.Abstractions/Models/Script.cs b/src/Umbraco.Abstractions/Models/Script.cs index b6e49f72ee..be96c04ddd 100644 --- a/src/Umbraco.Abstractions/Models/Script.cs +++ b/src/Umbraco.Abstractions/Models/Script.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core.Models /// [Serializable] [DataContract(IsReference = true)] - public class Script : File + public class Script : File, IScript { public Script(string path) : this(path, (Func) null) @@ -25,9 +25,6 @@ namespace Umbraco.Core.Models /// /// Overrides the default Entity identity check. /// - public override bool HasIdentity - { - get { return string.IsNullOrEmpty(Path) == false; } - } + public override bool HasIdentity => string.IsNullOrEmpty(Path) == false; } } diff --git a/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs index ea88cb7618..70226777b5 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs @@ -3,11 +3,14 @@ using Umbraco.Core.Models; namespace Umbraco.Core.Persistence.Repositories { - public interface IScriptRepository : IReadRepository, IWriteRepository