This commit is contained in:
Shannon Deminick
2013-03-21 20:31:23 +06:00
6 changed files with 157 additions and 52 deletions

View File

@@ -19,12 +19,36 @@ namespace Umbraco.Core.Xml
return source.Select(expr);
}
/// <summary>
/// Selects a list of XmlNode matching an XPath expression.
/// </summary>
/// <param name="source">A source XmlNode.</param>
/// <param name="expression">An XPath expression.</param>
/// <param name="variables">A set of XPathVariables.</param>
/// <returns>The list of XmlNode matching the XPath expression.</returns>
/// <remarks>
/// <para>If <param name="variables" /> is <c>null</c>, or is empty, or contains only one single
/// value which itself is <c>null</c>, then variables are ignored.</para>
/// <para>The XPath expression should reference variables as <c>$var</c>.</para>
/// </remarks>
public static XmlNodeList SelectNodes(this XmlNode source, string expression, IEnumerable<XPathVariable> variables)
{
var av = variables == null ? null : variables.ToArray();
return SelectNodes(source, expression, av);
}
/// <summary>
/// Selects a list of XmlNode matching an XPath expression.
/// </summary>
/// <param name="source">A source XmlNode.</param>
/// <param name="expression">An XPath expression.</param>
/// <param name="variables">A set of XPathVariables.</param>
/// <returns>The list of XmlNode matching the XPath expression.</returns>
/// <remarks>
/// <para>If <param name="variables" /> is <c>null</c>, or is empty, or contains only one single
/// value which itself is <c>null</c>, then variables are ignored.</para>
/// <para>The XPath expression should reference variables as <c>$var</c>.</para>
/// </remarks>
public static XmlNodeList SelectNodes(this XmlNode source, string expression, params XPathVariable[] variables)
{
if (variables == null || variables.Length == 0 || variables[0] == null)
@@ -34,12 +58,36 @@ namespace Umbraco.Core.Xml
return XmlNodeListFactory.CreateNodeList(iterator);
}
/// <summary>
/// Selects the first XmlNode that matches an XPath expression.
/// </summary>
/// <param name="source">A source XmlNode.</param>
/// <param name="expression">An XPath expression.</param>
/// <param name="variables">A set of XPathVariables.</param>
/// <returns>The first XmlNode that matches the XPath expression.</returns>
/// <remarks>
/// <para>If <param name="variables" /> is <c>null</c>, or is empty, or contains only one single
/// value which itself is <c>null</c>, then variables are ignored.</para>
/// <para>The XPath expression should reference variables as <c>$var</c>.</para>
/// </remarks>
public static XmlNode SelectSingleNode(this XmlNode source, string expression, IEnumerable<XPathVariable> variables)
{
var av = variables == null ? null : variables.ToArray();
return SelectSingleNode(source, expression, av);
}
/// <summary>
/// Selects the first XmlNode that matches an XPath expression.
/// </summary>
/// <param name="source">A source XmlNode.</param>
/// <param name="expression">An XPath expression.</param>
/// <param name="variables">A set of XPathVariables.</param>
/// <returns>The first XmlNode that matches the XPath expression.</returns>
/// <remarks>
/// <para>If <param name="variables" /> is <c>null</c>, or is empty, or contains only one single
/// value which itself is <c>null</c>, then variables are ignored.</para>
/// <para>The XPath expression should reference variables as <c>$var</c>.</para>
/// </remarks>
public static XmlNode SelectSingleNode(this XmlNode source, string expression, params XPathVariable[] variables)
{
if (variables == null || variables.Length == 0 || variables[0] == null)

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using Umbraco.Core.Models;
using Umbraco.Core.Xml;
namespace Umbraco.Web.PublishedCache
{
@@ -45,7 +46,12 @@ namespace Umbraco.Web.PublishedCache
/// <param name="xpath">The XPath query.</param>
/// <param name="vars">Optional XPath variables.</param>
/// <returns>The content, or null.</returns>
public virtual IPublishedContent GetSingleByXPath(string xpath, Core.Xml.XPathVariable[] vars)
/// <remarks>
/// <para>If <param name="vars" /> is <c>null</c>, or is empty, or contains only one single
/// value which itself is <c>null</c>, then variables are ignored.</para>
/// <para>The XPath expression should reference variables as <c>$var</c>.</para>
/// </remarks>
public virtual IPublishedContent GetSingleByXPath(string xpath, params XPathVariable[] vars)
{
return _cache.GetSingleByXPath(UmbracoContext, xpath, vars);
}
@@ -56,7 +62,12 @@ namespace Umbraco.Web.PublishedCache
/// <param name="xpath">The XPath query.</param>
/// <param name="vars">Optional XPath variables.</param>
/// <returns>The contents.</returns>
public virtual IEnumerable<IPublishedContent> GetByXPath(string xpath, Core.Xml.XPathVariable[] vars)
/// <remarks>
/// <para>If <param name="vars" /> is <c>null</c>, or is empty, or contains only one single
/// value which itself is <c>null</c>, then variables are ignored.</para>
/// <para>The XPath expression should reference variables as <c>$var</c>.</para>
/// </remarks>
public virtual IEnumerable<IPublishedContent> GetByXPath(string xpath, params XPathVariable[] vars)
{
return _cache.GetByXPath(UmbracoContext, xpath, vars);
}

View File

@@ -34,7 +34,7 @@ namespace Umbraco.Web.PublishedCache
/// Gets content identified by a route.
/// </summary>
/// <param name="route">The route</param>
/// <param name="hideTopLevelNode">FIXME</param>
/// <param name="hideTopLevelNode">A value forcing the HideTopLevelNode setting.</param>
/// <returns>The content, or null.</returns>
/// <remarks>A valid route is either a simple path eg <c>/foo/bar/nil</c> or a root node id and a path, eg <c>123/foo/bar/nil</c>.</remarks>
public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null)
@@ -51,11 +51,5 @@ namespace Umbraco.Web.PublishedCache
{
return _cache.GetRouteById(UmbracoContext, contentId);
}
// FIXME do we want that one here?
public IPublishedContent GetByUrlAlias(int rootNodeId, string alias)
{
return _cache.GetByUrlAlias(UmbracoContext, rootNodeId, alias);
}
}
}

View File

@@ -29,8 +29,5 @@ namespace Umbraco.Web.PublishedCache
/// <param name="contentId">The content unique identifier.</param>
/// <returns>The route.</returns>
string GetRouteById(UmbracoContext umbracoContext, int contentId);
// FIXME do we want that one?
IPublishedContent GetByUrlAlias(UmbracoContext umbracoContext, int rootNodeId, string alias);
}
}

View File

@@ -196,7 +196,6 @@ namespace Umbraco.Web.PublishedCache.LegacyXmlCache
public static string Root { get { return "/root"; } }
public string RootDocuments { get; private set; }
public string DescendantDocumentById { get; private set; }
public string DescendantDocumentByAlias { get; private set; }
public string ChildDocumentByUrlName { get; private set; }
public string ChildDocumentByUrlNameVar { get; private set; }
public string RootDocumentWithLowestSortOrder { get; private set; }
@@ -211,10 +210,6 @@ namespace Umbraco.Web.PublishedCache.LegacyXmlCache
case 0:
RootDocuments = "/root/node";
DescendantDocumentById = "//node [@id={0}]";
DescendantDocumentByAlias = "//node[("
+ "contains(concat(',',translate(data [@alias='umbracoUrlAlias'], ' ', ''),','),',{0},')"
+ " or contains(concat(',',translate(data [@alias='umbracoUrlAlias'], ' ', ''),','),',/{0},')"
+ ")]";
ChildDocumentByUrlName = "/node [@urlName='{0}']";
ChildDocumentByUrlNameVar = "/node [@urlName=${0}]";
RootDocumentWithLowestSortOrder = "/root/node [not(@sortOrder > ../node/@sortOrder)][1]";
@@ -224,10 +219,6 @@ namespace Umbraco.Web.PublishedCache.LegacyXmlCache
case 1:
RootDocuments = "/root/* [@isDoc]";
DescendantDocumentById = "//* [@isDoc and @id={0}]";
DescendantDocumentByAlias = "//* [@isDoc and ("
+ "contains(concat(',',translate(umbracoUrlAlias, ' ', ''),','),',{0},')"
+ " or contains(concat(',',translate(umbracoUrlAlias, ' ', ''),','),',/{0},')"
+ ")]";
ChildDocumentByUrlName = "/* [@isDoc and @urlName='{0}']";
ChildDocumentByUrlNameVar = "/* [@isDoc and @urlName=${0}]";
RootDocumentWithLowestSortOrder = "/root/* [@isDoc and not(@sortOrder > ../* [@isDoc]/@sortOrder)][1]";
@@ -307,36 +298,6 @@ namespace Umbraco.Web.PublishedCache.LegacyXmlCache
: xml.SelectNodes(xpath, vars);
return ConvertToDocuments(nodes);
}
// FIXME MOVE THAT ONE OUT OF HERE?
public IPublishedContent GetByUrlAlias(UmbracoContext umbracoContext, int rootNodeId, string alias)
{
if (alias == null) throw new ArgumentNullException("alias");
// the alias may be "foo/bar" or "/foo/bar"
// there may be spaces as in "/foo/bar, /foo/nil"
// these should probably be taken care of earlier on
alias = alias.TrimStart('/');
var xpathBuilder = new StringBuilder();
xpathBuilder.Append(XPathStringsDefinition.Root);
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 GetSingleByXPath(umbracoContext, xpath, var);
}
public bool HasContent()
{

View File

@@ -1,6 +1,11 @@
using System;
using System.Text;
using System.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core;
using Umbraco.Core.Xml;
using Umbraco.Web.PublishedCache;
namespace Umbraco.Web.Routing
{
@@ -24,7 +29,7 @@ namespace Umbraco.Web.Routing
if (docRequest.Uri.AbsolutePath != "/") // no alias if "/"
{
node = docRequest.RoutingContext.UmbracoContext.ContentCache.GetByUrlAlias(
node = FindContentByAlias(docRequest.RoutingContext.UmbracoContext.ContentCache,
docRequest.HasDomain ? docRequest.Domain.RootNodeId : 0,
docRequest.Uri.GetAbsolutePathDecoded());
@@ -37,5 +42,94 @@ namespace Umbraco.Web.Routing
return node != null;
}
private static IPublishedContent FindContentByAlias(ContextualPublishedContentCache cache, int rootNodeId, string alias)
{
if (alias == null) throw new ArgumentNullException("alias");
// the alias may be "foo/bar" or "/foo/bar"
// there may be spaces as in "/foo/bar, /foo/nil"
// these should probably be taken care of earlier on
alias = alias.TrimStart('/');
var xpathBuilder = new StringBuilder();
xpathBuilder.Append(XPathStringsDefinition.Root);
if (rootNodeId > 0)
xpathBuilder.AppendFormat(XPathStrings.DescendantDocumentById, rootNodeId);
XPathVariable var = null;
if (alias.Contains('\'') || alias.Contains('"'))
{
// use a var, as escaping gets ugly pretty quickly
var = new XPathVariable("alias", alias);
alias = "$alias";
}
xpathBuilder.AppendFormat(XPathStrings.DescendantDocumentByAlias, alias);
var xpath = xpathBuilder.ToString();
// note: it's OK if var is null, will be ignored
return cache.GetSingleByXPath(xpath, var);
}
#region XPath Strings
class XPathStringsDefinition
{
public int Version { get; private set; }
public static string Root { get { return "/root"; } }
public string DescendantDocumentById { get; private set; }
public string DescendantDocumentByAlias { get; private set; }
public XPathStringsDefinition(int version)
{
Version = version;
switch (version)
{
// legacy XML schema
case 0:
DescendantDocumentById = "//node [@id={0}]";
DescendantDocumentByAlias = "//node[("
+ "contains(concat(',',translate(data [@alias='umbracoUrlAlias'], ' ', ''),','),',{0},')"
+ " or contains(concat(',',translate(data [@alias='umbracoUrlAlias'], ' ', ''),','),',/{0},')"
+ ")]";
break;
// default XML schema as of 4.10
case 1:
DescendantDocumentById = "//* [@isDoc and @id={0}]";
DescendantDocumentByAlias = "//* [@isDoc and ("
+ "contains(concat(',',translate(umbracoUrlAlias, ' ', ''),','),',{0},')"
+ " or contains(concat(',',translate(umbracoUrlAlias, ' ', ''),','),',/{0},')"
+ ")]";
break;
default:
throw new Exception(string.Format("Unsupported Xml schema version '{0}').", version));
}
}
}
static XPathStringsDefinition _xPathStringsValue;
static XPathStringsDefinition XPathStrings
{
get
{
// in theory XPathStrings should be a static variable that
// we should initialize in a static ctor - but then test cases
// that switch schemas fail - so cache and refresh when needed,
// ie never when running the actual site
var version = global::umbraco.UmbracoSettings.UseLegacyXmlSchema ? 0 : 1;
if (_xPathStringsValue == null || _xPathStringsValue.Version != version)
_xPathStringsValue = new XPathStringsDefinition(version);
return _xPathStringsValue;
}
}
#endregion
}
}