U4-1611 - fix xpath special chars escaping issues in published content store
This commit is contained in:
@@ -724,6 +724,11 @@
|
||||
<Compile Include="WriteLock.cs" />
|
||||
<Compile Include="XmlExtensions.cs" />
|
||||
<Compile Include="XmlHelper.cs" />
|
||||
<Compile Include="Xml\DynamicContext.cs" />
|
||||
<Compile Include="Xml\XmlNamespaces.cs" />
|
||||
<Compile Include="Xml\XmlNodeListFactory.cs" />
|
||||
<Compile Include="Xml\XmlNodeExtensions.cs" />
|
||||
<Compile Include="Xml\XPathVariable.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
||||
312
src/Umbraco.Core/Xml/DynamicContext.cs
Normal file
312
src/Umbraco.Core/Xml/DynamicContext.cs
Normal file
@@ -0,0 +1,312 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using System.Xml.Xsl;
|
||||
using System.Xml.XPath;
|
||||
|
||||
// source: mvpxml.codeplex.com
|
||||
|
||||
namespace Umbraco.Core.Xml
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the evaluation context for fast execution and custom
|
||||
/// variables resolution.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class is responsible for resolving variables during dynamic expression execution.
|
||||
/// <para>Discussed in http://weblogs.asp.net/cazzu/archive/2003/10/07/30888.aspx</para>
|
||||
/// <para>Author: Daniel Cazzulino, <a href="http://clariusconsulting.net/kzu">blog</a></para>
|
||||
/// </remarks>
|
||||
public class DynamicContext : XsltContext
|
||||
{
|
||||
#region Private vars
|
||||
|
||||
readonly IDictionary<string, IXsltContextVariable> _variables =
|
||||
new Dictionary<string, IXsltContextVariable>();
|
||||
|
||||
#endregion Private
|
||||
|
||||
#region Constructors & Initialization
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DynamicContext"/> class.
|
||||
/// </summary>
|
||||
public DynamicContext()
|
||||
: base(new NameTable())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DynamicContext"/>
|
||||
/// class with the specified <see cref="NameTable"/>.
|
||||
/// </summary>
|
||||
/// <param name="table">The NameTable to use.</param>
|
||||
public DynamicContext(NameTable table)
|
||||
: base(table)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DynamicContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="context">A previously filled context with the namespaces to use.</param>
|
||||
public DynamicContext(XmlNamespaceManager context)
|
||||
: this(context, new NameTable())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DynamicContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="context">A previously filled context with the namespaces to use.</param>
|
||||
/// <param name="table">The NameTable to use.</param>
|
||||
public DynamicContext(XmlNamespaceManager context, NameTable table)
|
||||
: base(table)
|
||||
{
|
||||
object xml = table.Add(XmlNamespaces.Xml);
|
||||
object xmlns = table.Add(XmlNamespaces.XmlNs);
|
||||
|
||||
if (context == null) return;
|
||||
|
||||
foreach (string prefix in context)
|
||||
{
|
||||
var uri = context.LookupNamespace(prefix);
|
||||
// Use fast object reference comparison to omit forbidden namespace declarations.
|
||||
if (Equals(uri, xml) || Equals(uri, xmlns))
|
||||
continue;
|
||||
if (uri == null)
|
||||
continue;
|
||||
base.AddNamespace(prefix, uri);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Constructors & Initialization
|
||||
|
||||
#region Common Overrides
|
||||
|
||||
/// <summary>
|
||||
/// Implementation equal to <see cref="XsltContext"/>.
|
||||
/// </summary>
|
||||
public override int CompareDocument(string baseUri, string nextbaseUri)
|
||||
{
|
||||
return String.Compare(baseUri, nextbaseUri, false, System.Globalization.CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="XmlNamespaceManager"/>.
|
||||
/// </summary>
|
||||
public override string LookupNamespace(string prefix)
|
||||
{
|
||||
var key = NameTable.Get(prefix);
|
||||
return key == null ? null : base.LookupNamespace(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="XmlNamespaceManager"/>.
|
||||
/// </summary>
|
||||
public override string LookupPrefix(string uri)
|
||||
{
|
||||
var key = NameTable.Get(uri);
|
||||
return key == null ? null : base.LookupPrefix(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="XsltContext"/>.
|
||||
/// </summary>
|
||||
public override bool PreserveWhitespace(XPathNavigator node)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="XsltContext"/>.
|
||||
/// </summary>
|
||||
public override bool Whitespace
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
#endregion Common Overrides
|
||||
|
||||
#region Public Members
|
||||
|
||||
/// <summary>
|
||||
/// Shortcut method that compiles an expression using an empty navigator.
|
||||
/// </summary>
|
||||
/// <param name="xpath">The expression to compile</param>
|
||||
/// <returns>A compiled <see cref="XPathExpression"/>.</returns>
|
||||
public static XPathExpression Compile(string xpath)
|
||||
{
|
||||
return new XmlDocument().CreateNavigator().Compile(xpath);
|
||||
}
|
||||
|
||||
#endregion Public Members
|
||||
|
||||
#region Variable Handling Code
|
||||
|
||||
/// <summary>
|
||||
/// Adds the variable to the dynamic evaluation context.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the variable to add to the context.</param>
|
||||
/// <param name="value">The value of the variable to add to the context.</param>
|
||||
/// <remarks>
|
||||
/// Value type conversion for XPath evaluation is as follows:
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>CLR Type</term>
|
||||
/// <description>XPath type</description>
|
||||
/// </listheader>
|
||||
/// <item>
|
||||
/// <term>System.String</term>
|
||||
/// <description>XPathResultType.String</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>System.Double (or types that can be converted to)</term>
|
||||
/// <description>XPathResultType.Number</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>System.Boolean</term>
|
||||
/// <description>XPathResultType.Boolean</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>System.Xml.XPath.XPathNavigator</term>
|
||||
/// <description>XPathResultType.Navigator</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>System.Xml.XPath.XPathNodeIterator</term>
|
||||
/// <description>XPathResultType.NodeSet</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Others</term>
|
||||
/// <description>XPathResultType.Any</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// <note type="note">See the topic "Compile, Select, Evaluate, and Matches with
|
||||
/// XPath and XPathExpressions" in MSDN documentation for additional information.</note>
|
||||
/// </remarks>
|
||||
/// <exception cref="ArgumentNullException">The <paramref name="value"/> is null.</exception>
|
||||
public void AddVariable(string name, object value)
|
||||
{
|
||||
if (value == null) throw new ArgumentNullException("value");
|
||||
_variables[name] = new DynamicVariable(name, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="XsltContext"/>. Not used in our implementation.
|
||||
/// </summary>
|
||||
public override IXsltContextFunction ResolveFunction(string prefix, string name, XPathResultType[] argTypes)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the dynamic variables added to the context. See <see cref="XsltContext"/>.
|
||||
/// </summary>
|
||||
public override IXsltContextVariable ResolveVariable(string prefix, string name)
|
||||
{
|
||||
IXsltContextVariable var;
|
||||
_variables.TryGetValue(name, out var);
|
||||
return var;
|
||||
}
|
||||
|
||||
#endregion Variable Handling Code
|
||||
|
||||
#region Internal DynamicVariable class
|
||||
|
||||
/// <summary>
|
||||
/// Represents a variable during dynamic expression execution.
|
||||
/// </summary>
|
||||
internal class DynamicVariable : IXsltContextVariable
|
||||
{
|
||||
readonly string _name;
|
||||
readonly object _value;
|
||||
|
||||
#region Public Members
|
||||
|
||||
public string Name { get { return _name; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the variable.</param>
|
||||
/// <param name="value">The value of the variable.</param>
|
||||
public DynamicVariable(string name, object value)
|
||||
{
|
||||
|
||||
_name = name;
|
||||
_value = value;
|
||||
|
||||
if (value is String)
|
||||
_type = XPathResultType.String;
|
||||
else if (value is bool)
|
||||
_type = XPathResultType.Boolean;
|
||||
else if (value is XPathNavigator)
|
||||
_type = XPathResultType.Navigator;
|
||||
else if (value is XPathNodeIterator)
|
||||
_type = XPathResultType.NodeSet;
|
||||
else
|
||||
{
|
||||
// Try to convert to double (native XPath numeric type)
|
||||
if (value is double)
|
||||
{
|
||||
_type = XPathResultType.Number;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value is IConvertible)
|
||||
{
|
||||
try
|
||||
{
|
||||
_value = Convert.ToDouble(value);
|
||||
// We suceeded, so it's a number.
|
||||
_type = XPathResultType.Number;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
_type = XPathResultType.Any;
|
||||
}
|
||||
catch (OverflowException)
|
||||
{
|
||||
_type = XPathResultType.Any;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_type = XPathResultType.Any;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Public Members
|
||||
|
||||
#region IXsltContextVariable Implementation
|
||||
|
||||
XPathResultType IXsltContextVariable.VariableType
|
||||
{
|
||||
get { return _type; }
|
||||
}
|
||||
|
||||
readonly XPathResultType _type;
|
||||
|
||||
object IXsltContextVariable.Evaluate(XsltContext context)
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
bool IXsltContextVariable.IsLocal
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
bool IXsltContextVariable.IsParam
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
#endregion IXsltContextVariable Implementation
|
||||
}
|
||||
|
||||
#endregion Internal DynamicVariable class
|
||||
}
|
||||
}
|
||||
16
src/Umbraco.Core/Xml/XPathVariable.cs
Normal file
16
src/Umbraco.Core/Xml/XPathVariable.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
// source: mvpxml.codeplex.com
|
||||
|
||||
namespace Umbraco.Core.Xml
|
||||
{
|
||||
internal class XPathVariable
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Value { get; set; }
|
||||
|
||||
public XPathVariable(string name, string value)
|
||||
{
|
||||
Name = name;
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
src/Umbraco.Core/Xml/XmlNamespaces.cs
Normal file
41
src/Umbraco.Core/Xml/XmlNamespaces.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
// source: mvpxml.codeplex.com
|
||||
|
||||
namespace Umbraco.Core.Xml
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides public constants for wellknown XML namespaces.
|
||||
/// </summary>
|
||||
/// <remarks>Author: Daniel Cazzulino, <a href="http://clariusconsulting.net/kzu">blog</a></remarks>
|
||||
public static class XmlNamespaces
|
||||
{
|
||||
/// <summary>
|
||||
/// The public XML 1.0 namespace.
|
||||
/// </summary>
|
||||
/// <remarks>See http://www.w3.org/TR/2004/REC-xml-20040204/</remarks>
|
||||
public const string Xml = "http://www.w3.org/XML/1998/namespace";
|
||||
|
||||
/// <summary>
|
||||
/// Public Xml Namespaces specification namespace.
|
||||
/// </summary>
|
||||
/// <remarks>See http://www.w3.org/TR/REC-xml-names/</remarks>
|
||||
public const string XmlNs = "http://www.w3.org/2000/xmlns/";
|
||||
|
||||
/// <summary>
|
||||
/// Public Xml Namespaces prefix.
|
||||
/// </summary>
|
||||
/// <remarks>See http://www.w3.org/TR/REC-xml-names/</remarks>
|
||||
public const string XmlNsPrefix = "xmlns";
|
||||
|
||||
/// <summary>
|
||||
/// XML Schema instance namespace.
|
||||
/// </summary>
|
||||
/// <remarks>See http://www.w3.org/TR/xmlschema-1/</remarks>
|
||||
public const string Xsi = "http://www.w3.org/2001/XMLSchema-instance";
|
||||
|
||||
/// <summary>
|
||||
/// XML 1.0 Schema namespace.
|
||||
/// </summary>
|
||||
/// <remarks>See http://www.w3.org/TR/xmlschema-1/</remarks>
|
||||
public const string Xsd = "http://www.w3.org/2001/XMLSchema";
|
||||
}
|
||||
}
|
||||
51
src/Umbraco.Core/Xml/XmlNodeExtensions.cs
Normal file
51
src/Umbraco.Core/Xml/XmlNodeExtensions.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.XPath;
|
||||
|
||||
// source: mvpxml.codeplex.com
|
||||
|
||||
namespace Umbraco.Core.Xml
|
||||
{
|
||||
internal static class XmlNodeExtensions
|
||||
{
|
||||
static XPathNodeIterator Select(string expression, XPathNavigator source, params XPathVariable[] variables)
|
||||
{
|
||||
var expr = source.Compile(expression);
|
||||
var context = new DynamicContext();
|
||||
foreach (var variable in variables)
|
||||
context.AddVariable(variable.Name, variable.Value);
|
||||
expr.SetContext(context);
|
||||
return source.Select(expr);
|
||||
}
|
||||
|
||||
public static XmlNodeList SelectNodes(this XmlNode source, string expression, IEnumerable<XPathVariable> variables)
|
||||
{
|
||||
var av = variables == null ? null : variables.ToArray();
|
||||
return SelectNodes(source, expression, av);
|
||||
}
|
||||
|
||||
public static XmlNodeList SelectNodes(this XmlNode source, string expression, params XPathVariable[] variables)
|
||||
{
|
||||
if (variables == null || variables.Length == 0 || variables[0] == null)
|
||||
return source.SelectNodes(expression);
|
||||
|
||||
var iterator = Select(expression, source.CreateNavigator(), variables);
|
||||
return XmlNodeListFactory.CreateNodeList(iterator);
|
||||
}
|
||||
|
||||
public static XmlNode SelectSingleNode(this XmlNode source, string expression, IEnumerable<XPathVariable> variables)
|
||||
{
|
||||
var av = variables == null ? null : variables.ToArray();
|
||||
return SelectSingleNode(source, expression, av);
|
||||
}
|
||||
|
||||
public static XmlNode SelectSingleNode(this XmlNode source, string expression, params XPathVariable[] variables)
|
||||
{
|
||||
if (variables == null || variables.Length == 0 || variables[0] == null)
|
||||
return source.SelectSingleNode(expression);
|
||||
|
||||
return SelectNodes(source, expression, variables).Cast<XmlNode>().FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
178
src/Umbraco.Core/Xml/XmlNodeListFactory.cs
Normal file
178
src/Umbraco.Core/Xml/XmlNodeListFactory.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using System.Xml.XPath;
|
||||
|
||||
// source: mvpxml.codeplex.com
|
||||
|
||||
namespace Umbraco.Core.Xml
|
||||
{
|
||||
class XmlNodeListFactory
|
||||
{
|
||||
private XmlNodeListFactory() { }
|
||||
|
||||
#region Public members
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of a <see cref="XmlNodeList"/> that allows
|
||||
/// enumerating <see cref="XmlNode"/> elements in the iterator.
|
||||
/// </summary>
|
||||
/// <param name="iterator">The result of a previous node selection
|
||||
/// through an <see cref="XPathNavigator"/> query.</param>
|
||||
/// <returns>An initialized list ready to be enumerated.</returns>
|
||||
/// <remarks>The underlying XML store used to issue the query must be
|
||||
/// an object inheriting <see cref="XmlNode"/>, such as
|
||||
/// <see cref="XmlDocument"/>.</remarks>
|
||||
public static XmlNodeList CreateNodeList(XPathNodeIterator iterator)
|
||||
{
|
||||
return new XmlNodeListIterator(iterator);
|
||||
}
|
||||
|
||||
#endregion Public members
|
||||
|
||||
#region XmlNodeListIterator
|
||||
|
||||
private class XmlNodeListIterator : XmlNodeList
|
||||
{
|
||||
readonly XPathNodeIterator _iterator;
|
||||
readonly IList<XmlNode> _nodes = new List<XmlNode>();
|
||||
|
||||
public XmlNodeListIterator(XPathNodeIterator iterator)
|
||||
{
|
||||
_iterator = iterator.Clone();
|
||||
}
|
||||
|
||||
public override System.Collections.IEnumerator GetEnumerator()
|
||||
{
|
||||
return new XmlNodeListEnumerator(this);
|
||||
}
|
||||
|
||||
public override XmlNode Item(int index)
|
||||
{
|
||||
|
||||
if (index >= _nodes.Count)
|
||||
ReadTo(index);
|
||||
// Compatible behavior with .NET
|
||||
if (index >= _nodes.Count || index < 0)
|
||||
return null;
|
||||
return _nodes[index];
|
||||
}
|
||||
|
||||
public override int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_done) ReadToEnd();
|
||||
return _nodes.Count;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads the entire iterator.
|
||||
/// </summary>
|
||||
private void ReadToEnd()
|
||||
{
|
||||
while (_iterator.MoveNext())
|
||||
{
|
||||
var node = _iterator.Current as IHasXmlNode;
|
||||
// Check IHasXmlNode interface.
|
||||
if (node == null)
|
||||
throw new ArgumentException("IHasXmlNode is missing.");
|
||||
_nodes.Add(node.GetNode());
|
||||
}
|
||||
_done = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads up to the specified index, or until the
|
||||
/// iterator is consumed.
|
||||
/// </summary>
|
||||
private void ReadTo(int to)
|
||||
{
|
||||
while (_nodes.Count <= to)
|
||||
{
|
||||
if (_iterator.MoveNext())
|
||||
{
|
||||
var node = _iterator.Current as IHasXmlNode;
|
||||
// Check IHasXmlNode interface.
|
||||
if (node == null)
|
||||
throw new ArgumentException("IHasXmlNode is missing.");
|
||||
_nodes.Add(node.GetNode());
|
||||
}
|
||||
else
|
||||
{
|
||||
_done = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flags that the iterator has been consumed.
|
||||
/// </summary>
|
||||
private bool Done
|
||||
{
|
||||
get { return _done; }
|
||||
}
|
||||
|
||||
bool _done;
|
||||
|
||||
/// <summary>
|
||||
/// Current count of nodes in the iterator (read so far).
|
||||
/// </summary>
|
||||
private int CurrentPosition
|
||||
{
|
||||
get { return _nodes.Count; }
|
||||
}
|
||||
|
||||
#region XmlNodeListEnumerator
|
||||
|
||||
private class XmlNodeListEnumerator : System.Collections.IEnumerator
|
||||
{
|
||||
readonly XmlNodeListIterator _iterator;
|
||||
int _position = -1;
|
||||
|
||||
public XmlNodeListEnumerator(XmlNodeListIterator iterator)
|
||||
{
|
||||
_iterator = iterator;
|
||||
}
|
||||
|
||||
#region IEnumerator Members
|
||||
|
||||
void System.Collections.IEnumerator.Reset()
|
||||
{
|
||||
_position = -1;
|
||||
}
|
||||
|
||||
|
||||
bool System.Collections.IEnumerator.MoveNext()
|
||||
{
|
||||
_position++;
|
||||
_iterator.ReadTo(_position);
|
||||
|
||||
// If we reached the end and our index is still
|
||||
// bigger, there're no more items.
|
||||
if (_iterator.Done && _position >= _iterator.CurrentPosition)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
object System.Collections.IEnumerator.Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return _iterator[_position];
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion XmlNodeListEnumerator
|
||||
}
|
||||
|
||||
#endregion XmlNodeListIterator
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Xml;
|
||||
using Umbraco.Web.Routing;
|
||||
using umbraco;
|
||||
using umbraco.NodeFactory;
|
||||
@@ -119,19 +120,20 @@ namespace Umbraco.Web
|
||||
int pos = route.IndexOf('/');
|
||||
string path = pos == 0 ? route : route.Substring(pos);
|
||||
int startNodeId = pos == 0 ? 0 : int.Parse(route.Substring(0, pos));
|
||||
IEnumerable<XPathVariable> vars;
|
||||
|
||||
var xpath = CreateXpathQuery(startNodeId, path, hideTopLevelNode.Value);
|
||||
var xpath = CreateXpathQuery(startNodeId, path, hideTopLevelNode.Value, out vars);
|
||||
|
||||
//check if we can find the node in our xml cache
|
||||
var found = GetXml(umbracoContext).SelectSingleNode(xpath);
|
||||
var found = GetXml(umbracoContext).SelectSingleNode(xpath, vars);
|
||||
|
||||
// if hideTopLevelNodePath is true then for url /foo we looked for /*/foo
|
||||
// but maybe that was the url of a non-default top-level node, so we also
|
||||
// have to look for /foo (see note in NiceUrlProvider).
|
||||
if (found == null && hideTopLevelNode.Value && path.Length > 1 && path.IndexOf('/', 1) < 0)
|
||||
{
|
||||
xpath = CreateXpathQuery(startNodeId, path, false);
|
||||
found = GetXml(umbracoContext).SelectSingleNode(xpath);
|
||||
xpath = CreateXpathQuery(startNodeId, path, false, out vars);
|
||||
found = GetXml(umbracoContext).SelectSingleNode(xpath, vars);
|
||||
}
|
||||
|
||||
return ConvertToDocument(found);
|
||||
@@ -152,11 +154,19 @@ namespace Umbraco.Web
|
||||
|
||||
if (rootNodeId > 0)
|
||||
xpathBuilder.AppendFormat(XPathStrings.DescendantDocumentById, rootNodeId);
|
||||
|
||||
XPathVariable var = null;
|
||||
if (alias.Contains('\'') || alias.Contains('"'))
|
||||
{
|
||||
// use a var, escaping gets ugly pretty quickly
|
||||
var = new XPathVariable("alias", alias);
|
||||
alias = "$alias";
|
||||
}
|
||||
xpathBuilder.AppendFormat(XPathStrings.DescendantDocumentByAlias, alias);
|
||||
|
||||
var xpath = xpathBuilder.ToString();
|
||||
|
||||
return ConvertToDocument(GetXml(umbracoContext).SelectSingleNode(xpath));
|
||||
return ConvertToDocument(GetXml(umbracoContext).SelectSingleNode(xpath, var));
|
||||
}
|
||||
|
||||
public bool HasContent(UmbracoContext umbracoContext)
|
||||
@@ -177,9 +187,10 @@ namespace Umbraco.Web
|
||||
|
||||
static readonly char[] SlashChar = new char[] { '/' };
|
||||
|
||||
protected string CreateXpathQuery(int startNodeId, string path, bool hideTopLevelNodeFromPath)
|
||||
protected string CreateXpathQuery(int startNodeId, string path, bool hideTopLevelNodeFromPath, out IEnumerable<XPathVariable> vars)
|
||||
{
|
||||
string xpath;
|
||||
vars = null;
|
||||
|
||||
if (path == string.Empty || path == "/")
|
||||
{
|
||||
@@ -213,6 +224,7 @@ namespace Umbraco.Web
|
||||
var urlParts = path.Split(SlashChar, StringSplitOptions.RemoveEmptyEntries);
|
||||
var xpathBuilder = new StringBuilder();
|
||||
int partsIndex = 0;
|
||||
List<XPathVariable> varsList = null;
|
||||
|
||||
if (startNodeId == 0)
|
||||
{
|
||||
@@ -229,7 +241,16 @@ namespace Umbraco.Web
|
||||
|
||||
while (partsIndex < urlParts.Length)
|
||||
{
|
||||
xpathBuilder.AppendFormat(XPathStrings.ChildDocumentByUrlName, urlParts[partsIndex++]);
|
||||
var part = urlParts[partsIndex++];
|
||||
if (part.Contains('\'') || part.Contains('"'))
|
||||
{
|
||||
// use vars, escaping gets ugly pretty quickly
|
||||
varsList = varsList ?? new List<XPathVariable>();
|
||||
var varName = string.Format("var{0}", partsIndex);
|
||||
varsList.Add(new XPathVariable(varName, part));
|
||||
part = "$" + varName;
|
||||
}
|
||||
xpathBuilder.AppendFormat(XPathStrings.ChildDocumentByUrlName, part);
|
||||
}
|
||||
|
||||
xpath = xpathBuilder.ToString();
|
||||
|
||||
Reference in New Issue
Block a user