diff --git a/src/Umbraco.Core/Xml/XmlNodeExtensions.cs b/src/Umbraco.Core/Xml/XmlNodeExtensions.cs
index 53a7ef08db..2dbf8775f2 100644
--- a/src/Umbraco.Core/Xml/XmlNodeExtensions.cs
+++ b/src/Umbraco.Core/Xml/XmlNodeExtensions.cs
@@ -19,12 +19,36 @@ namespace Umbraco.Core.Xml
return source.Select(expr);
}
+ ///
+ /// Selects a list of XmlNode matching an XPath expression.
+ ///
+ /// A source XmlNode.
+ /// An XPath expression.
+ /// A set of XPathVariables.
+ /// The list of XmlNode matching the XPath expression.
+ ///
+ /// If is null, or is empty, or contains only one single
+ /// value which itself is null, then variables are ignored.
+ /// The XPath expression should reference variables as $var.
+ ///
public static XmlNodeList SelectNodes(this XmlNode source, string expression, IEnumerable variables)
{
var av = variables == null ? null : variables.ToArray();
return SelectNodes(source, expression, av);
}
+ ///
+ /// Selects a list of XmlNode matching an XPath expression.
+ ///
+ /// A source XmlNode.
+ /// An XPath expression.
+ /// A set of XPathVariables.
+ /// The list of XmlNode matching the XPath expression.
+ ///
+ /// If is null, or is empty, or contains only one single
+ /// value which itself is null, then variables are ignored.
+ /// The XPath expression should reference variables as $var.
+ ///
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);
}
+ ///
+ /// Selects the first XmlNode that matches an XPath expression.
+ ///
+ /// A source XmlNode.
+ /// An XPath expression.
+ /// A set of XPathVariables.
+ /// The first XmlNode that matches the XPath expression.
+ ///
+ /// If is null, or is empty, or contains only one single
+ /// value which itself is null, then variables are ignored.
+ /// The XPath expression should reference variables as $var.
+ ///
public static XmlNode SelectSingleNode(this XmlNode source, string expression, IEnumerable variables)
{
var av = variables == null ? null : variables.ToArray();
return SelectSingleNode(source, expression, av);
}
+ ///
+ /// Selects the first XmlNode that matches an XPath expression.
+ ///
+ /// A source XmlNode.
+ /// An XPath expression.
+ /// A set of XPathVariables.
+ /// The first XmlNode that matches the XPath expression.
+ ///
+ /// If is null, or is empty, or contains only one single
+ /// value which itself is null, then variables are ignored.
+ /// The XPath expression should reference variables as $var.
+ ///
public static XmlNode SelectSingleNode(this XmlNode source, string expression, params XPathVariable[] variables)
{
if (variables == null || variables.Length == 0 || variables[0] == null)
diff --git a/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs b/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs
index d5da24106d..fbaf997a85 100644
--- a/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs
+++ b/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs
@@ -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
/// The XPath query.
/// Optional XPath variables.
/// The content, or null.
- public virtual IPublishedContent GetSingleByXPath(string xpath, Core.Xml.XPathVariable[] vars)
+ ///
+ /// If is null, or is empty, or contains only one single
+ /// value which itself is null, then variables are ignored.
+ /// The XPath expression should reference variables as $var.
+ ///
+ public virtual IPublishedContent GetSingleByXPath(string xpath, params XPathVariable[] vars)
{
return _cache.GetSingleByXPath(UmbracoContext, xpath, vars);
}
@@ -56,7 +62,12 @@ namespace Umbraco.Web.PublishedCache
/// The XPath query.
/// Optional XPath variables.
/// The contents.
- public virtual IEnumerable GetByXPath(string xpath, Core.Xml.XPathVariable[] vars)
+ ///
+ /// If is null, or is empty, or contains only one single
+ /// value which itself is null, then variables are ignored.
+ /// The XPath expression should reference variables as $var.
+ ///
+ public virtual IEnumerable GetByXPath(string xpath, params XPathVariable[] vars)
{
return _cache.GetByXPath(UmbracoContext, xpath, vars);
}
diff --git a/src/Umbraco.Web/PublishedCache/ContextualPublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/ContextualPublishedContentCache.cs
index 03a7632512..bdbd0265b5 100644
--- a/src/Umbraco.Web/PublishedCache/ContextualPublishedContentCache.cs
+++ b/src/Umbraco.Web/PublishedCache/ContextualPublishedContentCache.cs
@@ -34,7 +34,7 @@ namespace Umbraco.Web.PublishedCache
/// Gets content identified by a route.
///
/// The route
- /// FIXME
+ /// A value forcing the HideTopLevelNode setting.
/// The content, or null.
/// A valid route is either a simple path eg /foo/bar/nil or a root node id and a path, eg 123/foo/bar/nil.
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);
- }
}
}
diff --git a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs
index f2898f7c68..3745271e43 100644
--- a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs
+++ b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs
@@ -29,8 +29,5 @@ namespace Umbraco.Web.PublishedCache
/// The content unique identifier.
/// The route.
string GetRouteById(UmbracoContext umbracoContext, int contentId);
-
- // FIXME do we want that one?
- IPublishedContent GetByUrlAlias(UmbracoContext umbracoContext, int rootNodeId, string alias);
}
}
diff --git a/src/Umbraco.Web/PublishedCache/LegacyXmlCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/LegacyXmlCache/PublishedContentCache.cs
index 43cae3f61c..5cf74137c6 100644
--- a/src/Umbraco.Web/PublishedCache/LegacyXmlCache/PublishedContentCache.cs
+++ b/src/Umbraco.Web/PublishedCache/LegacyXmlCache/PublishedContentCache.cs
@@ -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()
{
diff --git a/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs b/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs
index 9c8652d1d6..05e5b2c168 100644
--- a/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs
+++ b/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs
@@ -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
}
}
\ No newline at end of file