From be240758502333a6391c3e8500b2412ba20d1897 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 4 Apr 2013 05:03:01 -0200 Subject: [PATCH 01/14] presentation.library - get Xml navigator via Web.UmbracoContext --- src/Umbraco.Web/umbraco.presentation/library.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/umbraco.presentation/library.cs b/src/Umbraco.Web/umbraco.presentation/library.cs index 4e2a9a093e..466731bb92 100644 --- a/src/Umbraco.Web/umbraco.presentation/library.cs +++ b/src/Umbraco.Web/umbraco.presentation/library.cs @@ -1337,9 +1337,9 @@ namespace umbraco { try { - XPathNavigator xp = UmbracoContext.Current.GetXml().CreateNavigator(); - xp.MoveToId(HttpContext.Current.Items["pageID"].ToString()); - return xp.Select("."); + var nav = Umbraco.Web.UmbracoContext.Current.ContentCache.GetXPathNavigator(); + nav.MoveToId(HttpContext.Current.Items["pageID"].ToString()); + return nav.Select("."); } catch { From 1f999d7e22d860cbe58cc02416467cdae97ad1fd Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 4 Apr 2013 05:02:55 -0200 Subject: [PATCH 02/14] Macros - bugfix & refactor access to Xml cache --- src/Umbraco.Web/umbraco.presentation/macro.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/umbraco.presentation/macro.cs b/src/Umbraco.Web/umbraco.presentation/macro.cs index e6b420347d..ea31d49e87 100644 --- a/src/Umbraco.Web/umbraco.presentation/macro.cs +++ b/src/Umbraco.Web/umbraco.presentation/macro.cs @@ -769,7 +769,9 @@ namespace umbraco XmlDocument macroXml = null; // get master xml document - XmlDocument umbracoXml = umbraco.content.Instance.XmlContent; + var cache = UmbracoContext.Current.ContentCache.InnerCache as Umbraco.Web.PublishedCache.XmlPublishedCache.PublishedContentCache; + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); + XmlDocument umbracoXml = cache.GetXml(UmbracoContext.Current); macroXml = new XmlDocument(); macroXml.LoadXml(""); foreach (var prop in macro.Model.Properties) From ff6ee360da24231d6edd801e7fd1a9f2a5a4ae1a Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 4 Apr 2013 15:24:29 -0200 Subject: [PATCH 03/14] ItemRenderer - cleanup access to Xml cache --- .../umbraco/templateControls/ItemRenderer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs index 01a321d899..e3303d2823 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs @@ -104,8 +104,9 @@ namespace umbraco.presentation.templateControls if (tempNodeId != null && tempNodeId.Value != 0) { //moved the following from the catch block up as this will allow fallback options alt text etc to work - var xml = ((PublishedContentCache) PublishedContentCacheResolver.Current.ContentCache) - .GetXml(Umbraco.Web.UmbracoContext.Current); + var cache = Umbraco.Web.UmbracoContext.Current.ContentCache.InnerCache as PublishedContentCache; + if (cache == null) throw new InvalidOperationException("Unsupported IPublishedContentCache, only the Xml one is supported."); + var xml = cache.GetXml(Umbraco.Web.UmbracoContext.Current); var itemPage = new page(xml.GetElementById(tempNodeId.ToString())); tempElementContent = new item(itemPage.Elements, item.LegacyAttributes).FieldContent; } From 4201d678fae3f0e17d35ae6fc34d22b2c083c3e4 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 4 Apr 2013 15:24:29 -0200 Subject: [PATCH 04/14] XmlPublishedCache - GetXml() can be private, no need for internal --- .../PublishedCache/XmlPublishedCache/PublishedContentCache.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs index f4b17ecf7a..915c4fb55f 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs @@ -360,7 +360,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return GetXmlDelegate(umbracoContext.UmbracoUser, umbracoContext.InPreviewMode); } - internal XmlDocument GetXml() + private XmlDocument GetXml() { return GetXmlDelegate(null, false); } From f2d7e3549d3b2e6ae5165e478887015f4a4016f8 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 4 Apr 2013 15:24:29 -0200 Subject: [PATCH 05/14] XmlPublishedCache - fix casting exception messages --- src/Umbraco.Tests/LibraryTests.cs | 4 ++-- .../PublishedCache/PublishedContentCacheTests.cs | 2 +- src/Umbraco.Tests/PublishedContent/DynamicNodeTests.cs | 2 +- src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs | 2 +- src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs | 2 +- src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs | 2 +- src/Umbraco.Web/umbraco.presentation/UmbracoContext.cs | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Tests/LibraryTests.cs b/src/Umbraco.Tests/LibraryTests.cs index 6f1f13faa1..4880d3e2cd 100644 --- a/src/Umbraco.Tests/LibraryTests.cs +++ b/src/Umbraco.Tests/LibraryTests.cs @@ -94,8 +94,8 @@ namespace Umbraco.Tests private string LegacyGetItem(int nodeId, string alias) { var cache = UmbracoContext.Current.ContentCache.InnerCache as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the legacy one is supported."); - var umbracoXML = cache.GetXml(UmbracoContext.Current); // = UmbracoContext.Current.GetXml(); + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); + var umbracoXML = cache.GetXml(UmbracoContext.Current); string xpath = UmbracoSettings.UseLegacyXmlSchema ? "./data [@alias='{0}']" : "./{0}"; if (umbracoXML.GetElementById(nodeId.ToString()) != null) diff --git a/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs b/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs index f1c5db3d24..76c822643a 100644 --- a/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs +++ b/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs @@ -101,7 +101,7 @@ namespace Umbraco.Tests.PublishedCache Umbraco.Core.Configuration.UmbracoSettings.UseLegacyXmlSchema = true; var cache = _umbracoContext.ContentCache.InnerCache as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the legacy one is supported."); + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); cache.GetXmlDelegate = (user, preview) => { diff --git a/src/Umbraco.Tests/PublishedContent/DynamicNodeTests.cs b/src/Umbraco.Tests/PublishedContent/DynamicNodeTests.cs index b000466a85..054d21b244 100644 --- a/src/Umbraco.Tests/PublishedContent/DynamicNodeTests.cs +++ b/src/Umbraco.Tests/PublishedContent/DynamicNodeTests.cs @@ -86,7 +86,7 @@ namespace Umbraco.Tests.PublishedContent var ctx = GetUmbracoContext("/test", 1234); var cache = ctx.ContentCache.InnerCache as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the legacy one is supported."); + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); var node = new DynamicNode( new DynamicBackingItem( diff --git a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs b/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs index 9deaa12f7b..a64de740af 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs @@ -73,7 +73,7 @@ namespace Umbraco.Tests.Routing } var cache = routingContext.UmbracoContext.ContentCache.InnerCache as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the legacy one is supported."); + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); Assert.AreEqual(8, cachedRoutes.Count); diff --git a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs index 4ee5868e77..b01fd43c55 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs @@ -316,7 +316,7 @@ namespace Umbraco.Tests.Routing ignore = routingContext.UrlProvider.GetUrl(1002, new Uri("http://domain2.com"), false); var cache = routingContext.UmbracoContext.ContentCache.InnerCache as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the legacy one is supported."); + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); Assert.AreEqual(7, cachedRoutes.Count); diff --git a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs index 3e1eb98d3e..3dcb84eca3 100644 --- a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs @@ -39,7 +39,7 @@ namespace Umbraco.Tests.Routing // check that the proper route has been cached var cache = routingContext.UmbracoContext.ContentCache.InnerCache as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the legacy one is supported."); + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); Assert.AreEqual("10011/1001-1-1", cachedRoutes[100111]); diff --git a/src/Umbraco.Web/umbraco.presentation/UmbracoContext.cs b/src/Umbraco.Web/umbraco.presentation/UmbracoContext.cs index fe65aa25e4..dd355562a8 100644 --- a/src/Umbraco.Web/umbraco.presentation/UmbracoContext.cs +++ b/src/Umbraco.Web/umbraco.presentation/UmbracoContext.cs @@ -86,7 +86,7 @@ namespace umbraco.presentation var umbracoContext = Umbraco.Web.UmbracoContext.Current; var cache = umbracoContext.ContentCache.InnerCache as Umbraco.Web.PublishedCache.XmlPublishedCache.PublishedContentCache; if (cache == null) - throw new InvalidOperationException("ContentCache is not XmlPublishedCache.PublishedContentCache."); + throw new InvalidOperationException("Unsupported IPublishedContentCache, only the Xml one is supported."); return cache.GetXml(umbracoContext); } From 495b661263587553351c8add876b034b632bea82 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 10 Apr 2013 12:47:09 -0200 Subject: [PATCH 06/14] Core.Xml - refactor --- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../Xml/XPathNavigatorExtensions.cs | 30 +++++++++++++++++++ src/Umbraco.Core/Xml/XmlNodeExtensions.cs | 16 +++------- src/Umbraco.Core/XmlHelper.cs | 23 ++++++++++++++ 4 files changed, 58 insertions(+), 12 deletions(-) create mode 100644 src/Umbraco.Core/Xml/XPathNavigatorExtensions.cs diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 32132b0d6a..81276ea545 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -765,6 +765,7 @@ + diff --git a/src/Umbraco.Core/Xml/XPathNavigatorExtensions.cs b/src/Umbraco.Core/Xml/XPathNavigatorExtensions.cs new file mode 100644 index 0000000000..51b387b981 --- /dev/null +++ b/src/Umbraco.Core/Xml/XPathNavigatorExtensions.cs @@ -0,0 +1,30 @@ +using System.Xml.XPath; + +namespace Umbraco.Core.Xml +{ + /// + /// Provides extensions to XPathNavigator. + /// + internal static class XPathNavigatorExtensions + { + /// + /// Selects a node set, using the specified XPath expression. + /// + /// A source XPathNavigator. + /// An XPath expression. + /// A set of XPathVariables. + /// An iterator over the nodes matching the specified expression. + public static XPathNodeIterator Select(this XPathNavigator navigator, string expression, params XPathVariable[] variables) + { + if (variables == null || variables.Length == 0 || variables[0] == null) + return navigator.Select(expression); + + var compiled = navigator.Compile(expression); + var context = new DynamicContext(); + foreach (var variable in variables) + context.AddVariable(variable.Name, variable.Value); + compiled.SetContext(context); + return navigator.Select(compiled); + } + } +} diff --git a/src/Umbraco.Core/Xml/XmlNodeExtensions.cs b/src/Umbraco.Core/Xml/XmlNodeExtensions.cs index 2dbf8775f2..e6021f28d6 100644 --- a/src/Umbraco.Core/Xml/XmlNodeExtensions.cs +++ b/src/Umbraco.Core/Xml/XmlNodeExtensions.cs @@ -1,24 +1,16 @@ using System.Collections.Generic; using System.Linq; using System.Xml; -using System.Xml.XPath; // source: mvpxml.codeplex.com namespace Umbraco.Core.Xml { + /// + /// Provides extensions to XmlNode. + /// 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); - } - /// /// Selects a list of XmlNode matching an XPath expression. /// @@ -54,7 +46,7 @@ namespace Umbraco.Core.Xml if (variables == null || variables.Length == 0 || variables[0] == null) return source.SelectNodes(expression); - var iterator = Select(expression, source.CreateNavigator(), variables); + var iterator = source.CreateNavigator().Select(expression, variables); return XmlNodeListFactory.CreateNodeList(iterator); } diff --git a/src/Umbraco.Core/XmlHelper.cs b/src/Umbraco.Core/XmlHelper.cs index 3e67f2728e..89062b9eb1 100644 --- a/src/Umbraco.Core/XmlHelper.cs +++ b/src/Umbraco.Core/XmlHelper.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Xml; +using System.Xml.XPath; using Umbraco.Core.IO; namespace Umbraco.Core @@ -14,6 +15,28 @@ namespace Umbraco.Core /// 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))); + } public static string StripDashesInElementOrAttributeNames(string xml) { From 83308c3d5fcc2eb1dc2f3fbefb82d3b308d901c8 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 10 Apr 2013 12:47:11 -0200 Subject: [PATCH 07/14] Core.Xml - support XPathExpression --- .../Xml/XPathNavigatorExtensions.cs | 29 ++++++- src/Umbraco.Core/Xml/XmlNodeExtensions.cs | 82 ++++++++++++++++++- .../CoreXml/FrameworkXmlTests.cs | 22 +++++ .../ContextualPublishedCacheExtensions.cs | 27 ++++++ 4 files changed, 157 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Xml/XPathNavigatorExtensions.cs b/src/Umbraco.Core/Xml/XPathNavigatorExtensions.cs index 51b387b981..3839327e9c 100644 --- a/src/Umbraco.Core/Xml/XPathNavigatorExtensions.cs +++ b/src/Umbraco.Core/Xml/XPathNavigatorExtensions.cs @@ -19,7 +19,34 @@ namespace Umbraco.Core.Xml if (variables == null || variables.Length == 0 || variables[0] == null) return navigator.Select(expression); - var compiled = navigator.Compile(expression); + // Reflector shows that the standard XPathNavigator.Compile method just does + // return XPathExpression.Compile(xpath); + // only difference is, XPathNavigator.Compile is virtual so it could be overriden + // by a class inheriting from XPathNavigator... there does not seem to be any + // doing it in the Framework, though... so we'll assume it's much cleaner to use + // the static compile: + var compiled = XPathExpression.Compile(expression); + + var context = new DynamicContext(); + foreach (var variable in variables) + context.AddVariable(variable.Name, variable.Value); + compiled.SetContext(context); + return navigator.Select(compiled); + } + + /// + /// Selects a node set, using the specified XPath expression. + /// + /// A source XPathNavigator. + /// An XPath expression. + /// A set of XPathVariables. + /// An iterator over the nodes matching the specified expression. + public static XPathNodeIterator Select(this XPathNavigator navigator, XPathExpression expression, params XPathVariable[] variables) + { + if (variables == null || variables.Length == 0 || variables[0] == null) + return navigator.Select(expression); + + var compiled = expression.Clone(); // clone for thread-safety var context = new DynamicContext(); foreach (var variable in variables) context.AddVariable(variable.Name, variable.Value); diff --git a/src/Umbraco.Core/Xml/XmlNodeExtensions.cs b/src/Umbraco.Core/Xml/XmlNodeExtensions.cs index e6021f28d6..1af6ee0983 100644 --- a/src/Umbraco.Core/Xml/XmlNodeExtensions.cs +++ b/src/Umbraco.Core/Xml/XmlNodeExtensions.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Xml; +using System.Xml.XPath; // source: mvpxml.codeplex.com @@ -29,6 +30,24 @@ namespace Umbraco.Core.Xml 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, XPathExpression expression, IEnumerable variables) + { + var av = variables == null ? null : variables.ToArray(); + return SelectNodes(source, expression, av); + } + /// /// Selects a list of XmlNode matching an XPath expression. /// @@ -50,6 +69,27 @@ namespace Umbraco.Core.Xml return XmlNodeListFactory.CreateNodeList(iterator); } + /// + /// 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, XPathExpression expression, params XPathVariable[] variables) + { + if (variables == null || variables.Length == 0 || variables[0] == null) + return source.SelectNodes(expression); + + var iterator = source.CreateNavigator().Select(expression, variables); + return XmlNodeListFactory.CreateNodeList(iterator); + } + /// /// Selects the first XmlNode that matches an XPath expression. /// @@ -67,7 +107,25 @@ namespace Umbraco.Core.Xml 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, XPathExpression expression, IEnumerable variables) + { + var av = variables == null ? null : variables.ToArray(); + return SelectSingleNode(source, expression, av); + } + /// /// Selects the first XmlNode that matches an XPath expression. /// @@ -87,5 +145,25 @@ namespace Umbraco.Core.Xml return SelectNodes(source, expression, variables).Cast().FirstOrDefault(); } - } + + /// + /// 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, XPathExpression expression, params XPathVariable[] variables) + { + if (variables == null || variables.Length == 0 || variables[0] == null) + return source.SelectSingleNode(expression); + + return SelectNodes(source, expression, variables).Cast().FirstOrDefault(); + } + } } diff --git a/src/Umbraco.Tests/CoreXml/FrameworkXmlTests.cs b/src/Umbraco.Tests/CoreXml/FrameworkXmlTests.cs index 7ac4af2d20..a7cb581eed 100644 --- a/src/Umbraco.Tests/CoreXml/FrameworkXmlTests.cs +++ b/src/Umbraco.Tests/CoreXml/FrameworkXmlTests.cs @@ -213,5 +213,27 @@ namespace Umbraco.Tests.CoreXml Assert.AreEqual(5, count); } + + [Test] + public void OldFrameworkXPathBugIsFixed() + { + // see http://bytes.com/topic/net/answers/177129-reusing-xpathexpression-multiple-iterations + + var doc = new XmlDocument(); + doc.LoadXml(""); + + var nav = doc.CreateNavigator(); + var expr = nav.Compile("*"); + + nav.MoveToFirstChild(); //root + var iter1 = nav.Select(expr); + iter1.MoveNext(); //root/a + var iter2 = iter1.Current.Select(expr); + iter2.MoveNext(); // /root/a/a1 + iter2.MoveNext(); // /root/a/a2 + + // used to fail because iter1 and iter2 would conflict + Assert.IsTrue(iter1.MoveNext()); //root/b + } } } diff --git a/src/Umbraco.Web/ContextualPublishedCacheExtensions.cs b/src/Umbraco.Web/ContextualPublishedCacheExtensions.cs index 2a75c79c14..b2fb9089c4 100644 --- a/src/Umbraco.Web/ContextualPublishedCacheExtensions.cs +++ b/src/Umbraco.Web/ContextualPublishedCacheExtensions.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Xml.XPath; using Umbraco.Core.Dynamics; using Umbraco.Core.Xml; using Umbraco.Web.Models; @@ -36,6 +37,19 @@ namespace Umbraco.Web return content == null ? new DynamicNull() : new DynamicPublishedContent(content).AsDynamic(); } + /// + /// Gets a dynamic content resulting from an XPath query. + /// + /// The contextual cache. + /// The XPath query. + /// Optional XPath variables + /// The dynamic content, or null. + public static dynamic GetDynamicSingleByXPath(this ContextualPublishedContentCache cache, XPathExpression xpath, params XPathVariable[] vars) + { + var content = cache.GetSingleByXPath(xpath, vars); + return content == null ? new DynamicNull() : new DynamicPublishedContent(content).AsDynamic(); + } + /// /// Gets dynamic contents resulting from an XPath query. /// @@ -49,6 +63,19 @@ namespace Umbraco.Web return new DynamicPublishedContentList(content.Select(c => new DynamicPublishedContent(c))); } + /// + /// Gets dynamic contents resulting from an XPath query. + /// + /// The contextual cache. + /// The XPath query. + /// Optional XPath variables + /// The dynamic contents. + public static dynamic GetDynamicByXPath(this ContextualPublishedContentCache cache, XPathExpression xpath, params XPathVariable[] vars) + { + var content = cache.GetByXPath(xpath, vars); + return new DynamicPublishedContentList(content.Select(c => new DynamicPublishedContent(c))); + } + /// /// Gets dynamic contents at root. /// From 9982a0982648e746fda53ce12c86bd60a81e4aaa Mon Sep 17 00:00:00 2001 From: Stephan Date: Sun, 31 Mar 2013 18:40:55 -0200 Subject: [PATCH 08/14] Web.PublishedCache - merge resolvers so caches can be related --- .../PublishedContentTestBase.cs | 4 +- .../Cache/ContentTypeCacheRefresher.cs | 2 +- src/Umbraco.Web/Cache/DomainCacheRefresher.cs | 2 +- .../PublishedCache/IPublishedCaches.cs | 24 ++++++++++++ .../PublishedCache/PublishedCaches.cs | 29 +++++++++++++++ .../PublishedCache/PublishedCachesResolver.cs | 37 +++++++++++++++++++ .../PublishedContentCacheResolver.cs | 37 ------------------- .../PublishedMediaCacheResolver.cs | 37 ------------------- src/Umbraco.Web/Umbraco.Web.csproj | 5 ++- src/Umbraco.Web/UmbracoContext.cs | 4 +- src/Umbraco.Web/WebBootManager.cs | 9 +++-- 11 files changed, 104 insertions(+), 86 deletions(-) create mode 100644 src/Umbraco.Web/PublishedCache/IPublishedCaches.cs create mode 100644 src/Umbraco.Web/PublishedCache/PublishedCaches.cs create mode 100644 src/Umbraco.Web/PublishedCache/PublishedCachesResolver.cs delete mode 100644 src/Umbraco.Web/PublishedCache/PublishedContentCacheResolver.cs delete mode 100644 src/Umbraco.Web/PublishedCache/PublishedMediaCacheResolver.cs diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs index 4db386e3a4..b7957d17c1 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs @@ -47,8 +47,8 @@ namespace Umbraco.Tests.PublishedContent typeof(YesNoPropertyEditorValueConverter) }); - PublishedContentCacheResolver.Current = new PublishedContentCacheResolver(new PublishedContentCache()); - PublishedMediaCacheResolver.Current = new PublishedMediaCacheResolver(new PublishedMediaCache()); + PublishedCachesResolver.Current = new PublishedCachesResolver(new PublishedCaches( + new PublishedContentCache(), new PublishedMediaCache())); base.FreezeResolution(); } diff --git a/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs index 654d32bab3..6fac44e2cb 100644 --- a/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs @@ -215,7 +215,7 @@ namespace Umbraco.Web.Cache // are creating a nasty dependency - but keep it like that for the time being while // SD is cleaning cache refreshers up. - var contentCache = PublishedContentCacheResolver.Current.ContentCache as PublishedContentCache; + var contentCache = PublishedCachesResolver.Current.Caches.ContentCache as PublishedContentCache; if (contentCache != null) contentCache.RoutesCache.Clear(); } diff --git a/src/Umbraco.Web/Cache/DomainCacheRefresher.cs b/src/Umbraco.Web/Cache/DomainCacheRefresher.cs index c08d2f37a7..27a8a70eda 100644 --- a/src/Umbraco.Web/Cache/DomainCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/DomainCacheRefresher.cs @@ -48,7 +48,7 @@ namespace Umbraco.Web.Cache // are creating a nasty dependency - but keep it like that for the time being while // SD is cleaning cache refreshers up. - var contentCache = PublishedContentCacheResolver.Current.ContentCache as PublishedContentCache; + var contentCache = PublishedCachesResolver.Current.Caches.ContentCache as PublishedContentCache; if (contentCache != null) contentCache.RoutesCache.Clear(); } diff --git a/src/Umbraco.Web/PublishedCache/IPublishedCaches.cs b/src/Umbraco.Web/PublishedCache/IPublishedCaches.cs new file mode 100644 index 0000000000..21652d9ed2 --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/IPublishedCaches.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Umbraco.Web.PublishedCache +{ + /// + /// Provides caches (content and media). + /// + /// Groups caches that _may_ be related. + interface IPublishedCaches + { + /// + /// Gets the content cache. + /// + IPublishedContentCache ContentCache { get; } + + /// + /// Gets the media cache. + /// + IPublishedMediaCache MediaCache { get; } + } +} diff --git a/src/Umbraco.Web/PublishedCache/PublishedCaches.cs b/src/Umbraco.Web/PublishedCache/PublishedCaches.cs new file mode 100644 index 0000000000..146677418b --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/PublishedCaches.cs @@ -0,0 +1,29 @@ +namespace Umbraco.Web.PublishedCache +{ + /// + /// Provides caches (content and media). + /// + /// Default implementation for unrelated caches. + class PublishedCaches : IPublishedCaches + { + /// + /// Initializes a new instance of the class with a content cache + /// and a media cache. + /// + public PublishedCaches(IPublishedContentCache contentCache, IPublishedMediaCache mediaCache) + { + ContentCache = contentCache; + MediaCache = mediaCache; + } + + /// + /// Gets the content cache. + /// + public IPublishedContentCache ContentCache { get; private set; } + + /// + /// Gets the media cache. + /// + public IPublishedMediaCache MediaCache { get; private set; } + } +} diff --git a/src/Umbraco.Web/PublishedCache/PublishedCachesResolver.cs b/src/Umbraco.Web/PublishedCache/PublishedCachesResolver.cs new file mode 100644 index 0000000000..129b876a4f --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/PublishedCachesResolver.cs @@ -0,0 +1,37 @@ +using Umbraco.Core.ObjectResolution; + +namespace Umbraco.Web.PublishedCache +{ + /// + /// Resolves the IPublishedCaches object. + /// + internal sealed class PublishedCachesResolver : SingleObjectResolverBase + { + /// + /// Initializes a new instance of the class with caches. + /// + /// The caches. + /// The resolver is created by the WebBootManager and thus the constructor remains internal. + internal PublishedCachesResolver(IPublishedCaches caches) + : base(caches) + { } + + /// + /// Sets the caches. + /// + /// The caches. + /// For developers, at application startup. + public void SetCache(IPublishedCaches caches) + { + Value = caches; + } + + /// + /// Gets the caches. + /// + public IPublishedCaches Caches + { + get { return Value; } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PublishedCache/PublishedContentCacheResolver.cs b/src/Umbraco.Web/PublishedCache/PublishedContentCacheResolver.cs deleted file mode 100644 index b7e49e0b91..0000000000 --- a/src/Umbraco.Web/PublishedCache/PublishedContentCacheResolver.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Umbraco.Core.ObjectResolution; - -namespace Umbraco.Web.PublishedCache -{ - /// - /// Resolves the IPublishedContentCache object. - /// - internal sealed class PublishedContentCacheResolver : SingleObjectResolverBase - { - /// - /// Initializes a new instance of the class with a content cache. - /// - /// The content cache. - /// The resolver is created by the WebBootManager and thus the constructor remains internal. - internal PublishedContentCacheResolver(IPublishedContentCache publishedContentCache) - : base(publishedContentCache) - { } - - /// - /// Sets the content cache. - /// - /// The content cache. - /// For developers, at application startup. - public void SetContentCache(IPublishedContentCache contentCache) - { - Value = contentCache; - } - - /// - /// Gets the content cache. - /// - public IPublishedContentCache ContentCache - { - get { return Value; } - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/PublishedCache/PublishedMediaCacheResolver.cs b/src/Umbraco.Web/PublishedCache/PublishedMediaCacheResolver.cs deleted file mode 100644 index a3a0d47fb6..0000000000 --- a/src/Umbraco.Web/PublishedCache/PublishedMediaCacheResolver.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Umbraco.Core.ObjectResolution; - -namespace Umbraco.Web.PublishedCache -{ - /// - /// Resolves the IPublicMediaCache object. - /// - internal sealed class PublishedMediaCacheResolver : SingleObjectResolverBase - { - /// - /// Initializes a new instance of the class with a media cache. - /// - /// The media cache. - /// The resolver is created by the WebBootManager and thus the constructor remains internal. - internal PublishedMediaCacheResolver(IPublishedMediaCache publishedMediaCache) - : base(publishedMediaCache) - { } - - /// - /// Sets the media cache. - /// - /// The media cache. - /// For developers, at application startup. - public void SetContentCache(IPublishedMediaCache publishedMediaCache) - { - Value = publishedMediaCache; - } - - /// - /// Gets the media cache. - /// - public IPublishedMediaCache PublishedMediaCache - { - get { return Value; } - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a4f8fd5afa..d6b70cd7b7 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -332,6 +332,9 @@ + + + @@ -369,8 +372,6 @@ - - diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index 1038df0de5..958b3cb97e 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -89,8 +89,8 @@ namespace Umbraco.Web var umbracoContext = new UmbracoContext( httpContext, applicationContext, - PublishedContentCacheResolver.Current.ContentCache, - PublishedMediaCacheResolver.Current.PublishedMediaCache); + PublishedCachesResolver.Current.Caches.ContentCache, + PublishedCachesResolver.Current.Caches.MediaCache); // create the nice urls provider // there's one per request because there are some behavior parameters that can be changed diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index 3acba327db..0e571b4455 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -20,7 +20,6 @@ using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Umbraco.Web.PropertyEditors; using Umbraco.Web.PublishedCache; -using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Web.Routing; using Umbraco.Web.WebApi; using umbraco.BusinessLogic; @@ -277,8 +276,9 @@ namespace Umbraco.Web PropertyEditorValueConvertersResolver.Current.RemoveType(); PropertyEditorValueConvertersResolver.Current.AddType(); - PublishedContentCacheResolver.Current = new PublishedContentCacheResolver(new PublishedContentCache()); - PublishedMediaCacheResolver.Current = new PublishedMediaCacheResolver(new PublishedMediaCache()); + PublishedCachesResolver.Current = new PublishedCachesResolver(new PublishedCaches( + new PublishedCache.XmlPublishedCache.PublishedContentCache(), + new PublishedCache.XmlPublishedCache.PublishedMediaCache())); FilteredControllerFactoriesResolver.Current = new FilteredControllerFactoriesResolver( // add all known factories, devs can then modify this list on application @@ -313,7 +313,8 @@ namespace Umbraco.Web SiteDomainHelperResolver.Current = new SiteDomainHelperResolver(new SiteDomainHelper()); - PublishedContentCache.UnitTesting = _isForTesting; + // ain't that a bit dirty? + PublishedCache.XmlPublishedCache.PublishedContentCache.UnitTesting = _isForTesting; ThumbnailProvidersResolver.Current = new ThumbnailProvidersResolver( PluginManager.Current.ResolveThumbnailProviders()); From 70ad3edc1f3b7c47602afaf81975c433ddb4115c Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 3 Apr 2013 08:40:35 -0200 Subject: [PATCH 09/14] Web.UmbracoContext - refactor constructor to use IPublishedCaches --- .../PublishedCache/PublishedContentCacheTests.cs | 3 +-- .../TestHelpers/BaseDatabaseFactoryTest.cs | 3 +-- src/Umbraco.Web/UmbracoContext.cs | 15 ++++++--------- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs b/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs index 76c822643a..ec2dbdb265 100644 --- a/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs +++ b/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs @@ -90,8 +90,7 @@ namespace Umbraco.Tests.PublishedCache _umbracoContext = new UmbracoContext( _httpContextFactory.HttpContext, ApplicationContext.Current, - cache, - new PublishedMediaCache()); + new PublishedCaches(cache, new PublishedMediaCache())); _cache = _umbracoContext.ContentCache; } diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index a749765fa7..3c9db54741 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -298,8 +298,7 @@ namespace Umbraco.Tests.TestHelpers var ctx = new UmbracoContext( GetHttpContextFactory(url, routeData).HttpContext, ApplicationContext, - cache, - new PublishedMediaCache()); + new PublishedCaches(cache, new PublishedMediaCache())); return ctx; } diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index 958b3cb97e..d8bcf76fdd 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -89,8 +89,7 @@ namespace Umbraco.Web var umbracoContext = new UmbracoContext( httpContext, applicationContext, - PublishedCachesResolver.Current.Caches.ContentCache, - PublishedCachesResolver.Current.Caches.MediaCache); + PublishedCachesResolver.Current.Caches); // create the nice urls provider // there's one per request because there are some behavior parameters that can be changed @@ -118,14 +117,12 @@ namespace Umbraco.Web /// /// /// - /// The published content cache. - /// The published media cache. + /// The published caches. /// An optional value overriding detection of preview mode. internal UmbracoContext( HttpContextBase httpContext, ApplicationContext applicationContext, - IPublishedContentCache contentCache, - IPublishedMediaCache mediaCache, + IPublishedCaches publishedCaches, bool? preview = null) { if (httpContext == null) throw new ArgumentNullException("httpContext"); @@ -138,8 +135,8 @@ namespace Umbraco.Web Application = applicationContext; Security = new WebSecurity(); - ContentCache = new ContextualPublishedContentCache(contentCache, this); - MediaCache = new ContextualPublishedMediaCache(mediaCache, this); + ContentCache = new ContextualPublishedContentCache(publishedCaches.ContentCache, this); + MediaCache = new ContextualPublishedMediaCache(publishedCaches.MediaCache, this); InPreviewMode = preview ?? DetectInPreviewModeFromRequest(); // set the urls... @@ -245,7 +242,7 @@ namespace Umbraco.Web /// internal ContextualPublishedMediaCache MediaCache { get; private set; } - /// + /// /// Boolean value indicating whether the current request is a front-end umbraco request /// public bool IsFrontEndUmbracoRequest From b89951dd11fbd0243bdaf951245f99e0cacf7df1 Mon Sep 17 00:00:00 2001 From: Stephan Date: Sun, 31 Mar 2013 18:44:29 -0200 Subject: [PATCH 10/14] Web.PublishedCache - refactor contextual caches & inner caches management --- .../ContextualPublishedCache.cs | 38 ++----- .../ContextualPublishedCacheOfT.cs | 105 ++++++++++++++++++ .../ContextualPublishedContentCache.cs | 18 +-- .../ContextualPublishedMediaCache.cs | 8 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 5 files changed, 123 insertions(+), 47 deletions(-) create mode 100644 src/Umbraco.Web/PublishedCache/ContextualPublishedCacheOfT.cs diff --git a/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs b/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs index 88e585775f..b965f09c16 100644 --- a/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs +++ b/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs @@ -14,12 +14,14 @@ namespace Umbraco.Web.PublishedCache internal abstract class ContextualPublishedCache { protected readonly UmbracoContext UmbracoContext; - private readonly IPublishedCache _cache; - protected ContextualPublishedCache(UmbracoContext umbracoContext, IPublishedCache cache) + /// + /// Initializes a new instance of the with a context. + /// + /// The context. + protected ContextualPublishedCache(UmbracoContext umbracoContext) { UmbracoContext = umbracoContext; - _cache = cache; } /// @@ -27,19 +29,13 @@ namespace Umbraco.Web.PublishedCache /// /// The content unique identifier. /// The content, or null. - public virtual IPublishedContent GetById(int contentId) - { - return _cache.GetById(UmbracoContext, contentId); - } + public abstract IPublishedContent GetById(int contentId); /// /// Gets contents at root. /// /// The contents. - public virtual IEnumerable GetAtRoot() - { - return _cache.GetAtRoot(UmbracoContext); - } + public abstract IEnumerable GetAtRoot(); /// /// Gets a content resulting from an XPath query. @@ -52,10 +48,7 @@ namespace Umbraco.Web.PublishedCache /// 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); - } + public abstract IPublishedContent GetSingleByXPath(string xpath, params XPathVariable[] vars); /// /// Gets contents resulting from an XPath query. @@ -68,27 +61,18 @@ namespace Umbraco.Web.PublishedCache /// 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); - } + public abstract IEnumerable GetByXPath(string xpath, params XPathVariable[] vars); /// /// Gets an XPath navigator that can be used to navigate contents. /// /// The XPath navigator. - public virtual XPathNavigator GetXPathNavigator() - { - return _cache.GetXPathNavigator(UmbracoContext); - } + public abstract XPathNavigator GetXPathNavigator(); /// /// Gets a value indicating whether the underlying non-contextual cache contains published content. /// /// A value indicating whether the underlying non-contextual cache contains published content. - public virtual bool HasContent() - { - return _cache.HasContent(); - } + public abstract bool HasContent(); } } diff --git a/src/Umbraco.Web/PublishedCache/ContextualPublishedCacheOfT.cs b/src/Umbraco.Web/PublishedCache/ContextualPublishedCacheOfT.cs new file mode 100644 index 0000000000..00ad44860d --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/ContextualPublishedCacheOfT.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.XPath; +using Umbraco.Core.Models; +using Umbraco.Core.Xml; + +namespace Umbraco.Web.PublishedCache +{ + /// + /// Provides access to cached contents in a specified context. + /// + /// The type of the underlying published cache. + internal abstract class ContextualPublishedCache : ContextualPublishedCache + where T : IPublishedCache + { + private readonly T _cache; + + /// + /// Initializes a new instance of the with a context and a published cache. + /// + /// The context. + /// The cache. + protected ContextualPublishedCache(UmbracoContext umbracoContext, T cache) + : base(umbracoContext) + { + _cache = cache; + } + + /// + /// Gets the underlying published cache. + /// + public T InnerCache { get { return _cache; } } + + /// + /// Gets a content identified by its unique identifier. + /// + /// The content unique identifier. + /// The content, or null. + public override IPublishedContent GetById(int contentId) + { + return _cache.GetById(UmbracoContext, contentId); + } + + /// + /// Gets contents at root. + /// + /// The contents. + public override IEnumerable GetAtRoot() + { + return _cache.GetAtRoot(UmbracoContext); + } + + /// + /// Gets a content resulting from an XPath query. + /// + /// The XPath query. + /// Optional XPath variables. + /// The content, or null. + /// + /// 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 override IPublishedContent GetSingleByXPath(string xpath, params XPathVariable[] vars) + { + return _cache.GetSingleByXPath(UmbracoContext, xpath, vars); + } + + /// + /// Gets contents resulting from an XPath query. + /// + /// The XPath query. + /// Optional XPath variables. + /// The contents. + /// + /// 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 override IEnumerable GetByXPath(string xpath, params XPathVariable[] vars) + { + return _cache.GetByXPath(UmbracoContext, xpath, vars); + } + + /// + /// Gets an XPath navigator that can be used to navigate contents. + /// + /// The XPath navigator. + public override XPathNavigator GetXPathNavigator() + { + return _cache.GetXPathNavigator(UmbracoContext); + } + + /// + /// Gets a value indicating whether the underlying non-contextual cache contains published content. + /// + /// A value indicating whether the underlying non-contextual cache contains published content. + public override bool HasContent() + { + return _cache.HasContent(UmbracoContext); + } + } +} diff --git a/src/Umbraco.Web/PublishedCache/ContextualPublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/ContextualPublishedContentCache.cs index bdbd0265b5..728f114cce 100644 --- a/src/Umbraco.Web/PublishedCache/ContextualPublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/ContextualPublishedContentCache.cs @@ -9,10 +9,8 @@ namespace Umbraco.Web.PublishedCache /// /// Provides access to cached documents in a specified context. /// - internal class ContextualPublishedContentCache : ContextualPublishedCache + internal class ContextualPublishedContentCache : ContextualPublishedCache { - private readonly IPublishedContentCache _cache; - /// /// Initializes a new instance of the class with a published content cache and a context. /// @@ -20,15 +18,7 @@ namespace Umbraco.Web.PublishedCache /// A context. public ContextualPublishedContentCache(IPublishedContentCache cache, UmbracoContext umbracoContext) : base(umbracoContext, cache) - { - _cache = cache; - } - - /// - /// Gets the inner IPublishedContentCache. - /// - /// For unit tests. - internal IPublishedContentCache InnerCache { get { return _cache; } } + { } /// /// Gets content identified by a route. @@ -39,7 +29,7 @@ namespace Umbraco.Web.PublishedCache /// 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) { - return _cache.GetByRoute(UmbracoContext, route, hideTopLevelNode); + return InnerCache.GetByRoute(UmbracoContext, route, hideTopLevelNode); } /// @@ -49,7 +39,7 @@ namespace Umbraco.Web.PublishedCache /// The route. public string GetRouteById(int contentId) { - return _cache.GetRouteById(UmbracoContext, contentId); + return InnerCache.GetRouteById(UmbracoContext, contentId); } } } diff --git a/src/Umbraco.Web/PublishedCache/ContextualPublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/ContextualPublishedMediaCache.cs index c98cf5792c..f3621895e4 100644 --- a/src/Umbraco.Web/PublishedCache/ContextualPublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/ContextualPublishedMediaCache.cs @@ -9,10 +9,8 @@ namespace Umbraco.Web.PublishedCache /// /// Provides access to cached medias in a specified context. /// - internal class ContextualPublishedMediaCache : ContextualPublishedCache + internal class ContextualPublishedMediaCache : ContextualPublishedCache { - private readonly IPublishedMediaCache _cache; - /// /// Initializes a new instance of the class with a published media cache and a context. /// @@ -20,8 +18,6 @@ namespace Umbraco.Web.PublishedCache /// A context. public ContextualPublishedMediaCache(IPublishedMediaCache cache, UmbracoContext umbracoContext) : base(umbracoContext, cache) - { - _cache = cache; - } + { } } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index d6b70cd7b7..120b916c8a 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -330,6 +330,7 @@ + From efee4d0c0c3ef20f719cfdc19d8224588be4de19 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 4 Apr 2013 05:09:26 -0200 Subject: [PATCH 11/14] Web.PublishedCache - IPublishedCaches creates contextual caches --- .../PublishedCache/IPublishedCaches.cs | 14 +++++++++++++ .../PublishedCache/PublishedCaches.cs | 20 +++++++++++++++++++ src/Umbraco.Web/UmbracoContext.cs | 4 ++-- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/IPublishedCaches.cs b/src/Umbraco.Web/PublishedCache/IPublishedCaches.cs index 21652d9ed2..3125656c79 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedCaches.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedCaches.cs @@ -20,5 +20,19 @@ namespace Umbraco.Web.PublishedCache /// Gets the media cache. /// IPublishedMediaCache MediaCache { get; } + + /// + /// Creates a contextual content cache for a specified context. + /// + /// The context. + /// A new contextual content cache for the specified context. + ContextualPublishedContentCache CreateContextualContentCache(UmbracoContext context); + + /// + /// Creates a contextual media cache for a specified context. + /// + /// The context. + /// A new contextual media cache for the specified context. + ContextualPublishedMediaCache CreateContextualMediaCache(UmbracoContext context); } } diff --git a/src/Umbraco.Web/PublishedCache/PublishedCaches.cs b/src/Umbraco.Web/PublishedCache/PublishedCaches.cs index 146677418b..7aa7e2f501 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedCaches.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedCaches.cs @@ -25,5 +25,25 @@ /// Gets the media cache. /// public IPublishedMediaCache MediaCache { get; private set; } + + /// + /// Creates a contextual content cache for a specified context. + /// + /// The context. + /// A new contextual content cache for the specified context. + public ContextualPublishedContentCache CreateContextualContentCache(UmbracoContext context) + { + return new ContextualPublishedContentCache(ContentCache, context); + } + + /// + /// Creates a contextual media cache for a specified context. + /// + /// The context. + /// A new contextual media cache for the specified context. + public ContextualPublishedMediaCache CreateContextualMediaCache(UmbracoContext context) + { + return new ContextualPublishedMediaCache(MediaCache, context); + } } } diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index d8bcf76fdd..7c5663660a 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -135,8 +135,8 @@ namespace Umbraco.Web Application = applicationContext; Security = new WebSecurity(); - ContentCache = new ContextualPublishedContentCache(publishedCaches.ContentCache, this); - MediaCache = new ContextualPublishedMediaCache(publishedCaches.MediaCache, this); + ContentCache = publishedCaches.CreateContextualContentCache(this); + MediaCache = publishedCaches.CreateContextualMediaCache(this); InPreviewMode = preview ?? DetectInPreviewModeFromRequest(); // set the urls... From c5b6bb52e0567dee1fb82be84fdf74a867e894b4 Mon Sep 17 00:00:00 2001 From: Stephan Date: Sun, 31 Mar 2013 18:47:25 -0200 Subject: [PATCH 12/14] Web.PublishedCache - refactor how caches manage preview --- src/Umbraco.Tests/LibraryTests.cs | 2 +- .../PublishedContent/DynamicNodeTests.cs | 2 +- .../ContextualPublishedCache.cs | 101 ++++++++++++++++-- .../ContextualPublishedCacheOfT.cs | 40 ++++--- .../ContextualPublishedContentCache.cs | 35 +++++- .../PublishedCache/IPublishedCache.cs | 29 +++-- .../PublishedCache/IPublishedContentCache.cs | 8 +- .../PublishedContentCache.cs | 80 +++++--------- .../XmlPublishedCache/PublishedMediaCache.cs | 12 +-- .../umbraco.presentation/UmbracoContext.cs | 2 +- src/Umbraco.Web/umbraco.presentation/macro.cs | 2 +- .../umbraco/templateControls/ItemRenderer.cs | 2 +- 12 files changed, 213 insertions(+), 102 deletions(-) diff --git a/src/Umbraco.Tests/LibraryTests.cs b/src/Umbraco.Tests/LibraryTests.cs index 4880d3e2cd..77fe2afe96 100644 --- a/src/Umbraco.Tests/LibraryTests.cs +++ b/src/Umbraco.Tests/LibraryTests.cs @@ -95,7 +95,7 @@ namespace Umbraco.Tests { var cache = UmbracoContext.Current.ContentCache.InnerCache as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); - var umbracoXML = cache.GetXml(UmbracoContext.Current); + var umbracoXML = cache.GetXml(UmbracoContext.Current, UmbracoContext.Current.InPreviewMode); string xpath = UmbracoSettings.UseLegacyXmlSchema ? "./data [@alias='{0}']" : "./{0}"; if (umbracoXML.GetElementById(nodeId.ToString()) != null) diff --git a/src/Umbraco.Tests/PublishedContent/DynamicNodeTests.cs b/src/Umbraco.Tests/PublishedContent/DynamicNodeTests.cs index 054d21b244..011ed05d0a 100644 --- a/src/Umbraco.Tests/PublishedContent/DynamicNodeTests.cs +++ b/src/Umbraco.Tests/PublishedContent/DynamicNodeTests.cs @@ -90,7 +90,7 @@ namespace Umbraco.Tests.PublishedContent var node = new DynamicNode( new DynamicBackingItem( - new Node(cache.GetXml(ctx).SelectSingleNode("//*[@id='" + id + "' and @isDoc]")))); + new Node(cache.GetXml(ctx, ctx.InPreviewMode).SelectSingleNode("//*[@id='" + id + "' and @isDoc]")))); Assert.IsNotNull(node); return (dynamic)node; } diff --git a/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs b/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs index b965f09c16..6436384ffa 100644 --- a/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs +++ b/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs @@ -29,13 +29,36 @@ namespace Umbraco.Web.PublishedCache /// /// The content unique identifier. /// The content, or null. - public abstract IPublishedContent GetById(int contentId); + /// Considers published or unpublished content depending on context. + public IPublishedContent GetById(int contentId) + { + return GetById(UmbracoContext.InPreviewMode, contentId); + } + + /// + /// Gets a content identified by its unique identifier. + /// + /// A value indicating whether to consider unpublished content. + /// The content unique identifier. + /// The content, or null. + public abstract IPublishedContent GetById(bool preview, int contentId); + + /// + /// Gets content at root. + /// + /// The contents. + /// Considers published or unpublished content depending on context. + public IEnumerable GetAtRoot() + { + return GetAtRoot(UmbracoContext.InPreviewMode); + } /// /// Gets contents at root. /// + /// A value indicating whether to consider unpublished content. /// The contents. - public abstract IEnumerable GetAtRoot(); + public abstract IEnumerable GetAtRoot(bool preview); /// /// Gets a content resulting from an XPath query. @@ -47,11 +70,29 @@ namespace Umbraco.Web.PublishedCache /// 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. + /// Considers published or unpublished content depending on context. /// - public abstract IPublishedContent GetSingleByXPath(string xpath, params XPathVariable[] vars); + public IPublishedContent GetSingleByXPath(string xpath, params XPathVariable[] vars) + { + return GetSingleByXPath(UmbracoContext.InPreviewMode, xpath, vars); + } /// - /// Gets contents resulting from an XPath query. + /// Gets a content resulting from an XPath query. + /// + /// A value indicating whether to consider unpublished content. + /// The XPath query. + /// Optional XPath variables. + /// The content, or null. + /// + /// 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 abstract IPublishedContent GetSingleByXPath(bool preview, string xpath, params XPathVariable[] vars); + + /// + /// Gets content resulting from an XPath query. /// /// The XPath query. /// Optional XPath variables. @@ -60,19 +101,59 @@ namespace Umbraco.Web.PublishedCache /// 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. + /// Considers published or unpublished content depending on context. /// - public abstract IEnumerable GetByXPath(string xpath, params XPathVariable[] vars); + public IEnumerable GetByXPath(string xpath, params XPathVariable[] vars) + { + return GetByXPath(UmbracoContext.InPreviewMode, xpath, vars); + } /// - /// Gets an XPath navigator that can be used to navigate contents. + /// Gets content resulting from an XPath query. + /// + /// A value indicating whether to consider unpublished content. + /// The XPath query. + /// Optional XPath variables. + /// The contents. + /// + /// 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 abstract IEnumerable GetByXPath(bool preview, string xpath, params XPathVariable[] vars); + + /// + /// Gets an XPath navigator that can be used to navigate content. /// /// The XPath navigator. - public abstract XPathNavigator GetXPathNavigator(); + /// Considers published or unpublished content depending on context. + public XPathNavigator GetXPathNavigator() + { + return GetXPathNavigator(UmbracoContext.InPreviewMode); + } /// - /// Gets a value indicating whether the underlying non-contextual cache contains published content. + /// Gets an XPath navigator that can be used to navigate content. /// - /// A value indicating whether the underlying non-contextual cache contains published content. - public abstract bool HasContent(); + /// A value indicating whether to consider unpublished content. + /// The XPath navigator. + public abstract XPathNavigator GetXPathNavigator(bool preview); + + /// + /// Gets a value indicating whether the underlying non-contextual cache contains content. + /// + /// A value indicating whether the underlying non-contextual cache contains content. + /// Considers published or unpublished content depending on context. + public bool HasContent() + { + return HasContent(UmbracoContext.InPreviewMode); + } + + /// + /// Gets a value indicating whether the underlying non-contextual cache contains content. + /// + /// A value indicating whether to consider unpublished content. + /// A value indicating whether the underlying non-contextual cache contains content. + public abstract bool HasContent(bool preview); } } diff --git a/src/Umbraco.Web/PublishedCache/ContextualPublishedCacheOfT.cs b/src/Umbraco.Web/PublishedCache/ContextualPublishedCacheOfT.cs index 00ad44860d..a279026e86 100644 --- a/src/Umbraco.Web/PublishedCache/ContextualPublishedCacheOfT.cs +++ b/src/Umbraco.Web/PublishedCache/ContextualPublishedCacheOfT.cs @@ -36,25 +36,28 @@ namespace Umbraco.Web.PublishedCache /// /// Gets a content identified by its unique identifier. /// + /// A value indicating whether to consider unpublished content. /// The content unique identifier. /// The content, or null. - public override IPublishedContent GetById(int contentId) + public override IPublishedContent GetById(bool preview, int contentId) { - return _cache.GetById(UmbracoContext, contentId); + return _cache.GetById(UmbracoContext, preview, contentId); } /// - /// Gets contents at root. + /// Gets content at root. /// + /// A value indicating whether to consider unpublished content. /// The contents. - public override IEnumerable GetAtRoot() + public override IEnumerable GetAtRoot(bool preview) { - return _cache.GetAtRoot(UmbracoContext); + return _cache.GetAtRoot(UmbracoContext, preview); } /// /// Gets a content resulting from an XPath query. /// + /// A value indicating whether to consider unpublished content. /// The XPath query. /// Optional XPath variables. /// The content, or null. @@ -63,14 +66,15 @@ namespace Umbraco.Web.PublishedCache /// value which itself is null, then variables are ignored. /// The XPath expression should reference variables as $var. /// - public override IPublishedContent GetSingleByXPath(string xpath, params XPathVariable[] vars) + public override IPublishedContent GetSingleByXPath(bool preview, string xpath, params XPathVariable[] vars) { - return _cache.GetSingleByXPath(UmbracoContext, xpath, vars); + return _cache.GetSingleByXPath(UmbracoContext, preview, xpath, vars); } /// - /// Gets contents resulting from an XPath query. + /// Gets content resulting from an XPath query. /// + /// A value indicating whether to consider unpublished content. /// The XPath query. /// Optional XPath variables. /// The contents. @@ -79,27 +83,29 @@ namespace Umbraco.Web.PublishedCache /// value which itself is null, then variables are ignored. /// The XPath expression should reference variables as $var. /// - public override IEnumerable GetByXPath(string xpath, params XPathVariable[] vars) + public override IEnumerable GetByXPath(bool preview, string xpath, params XPathVariable[] vars) { - return _cache.GetByXPath(UmbracoContext, xpath, vars); + return _cache.GetByXPath(UmbracoContext, preview, xpath, vars); } /// - /// Gets an XPath navigator that can be used to navigate contents. + /// Gets an XPath navigator that can be used to navigate content. /// + /// A value indicating whether to consider unpublished content. /// The XPath navigator. - public override XPathNavigator GetXPathNavigator() + public override XPathNavigator GetXPathNavigator(bool preview) { - return _cache.GetXPathNavigator(UmbracoContext); + return _cache.GetXPathNavigator(UmbracoContext, preview); } /// - /// Gets a value indicating whether the underlying non-contextual cache contains published content. + /// Gets a value indicating whether the underlying non-contextual cache contains content. /// - /// A value indicating whether the underlying non-contextual cache contains published content. - public override bool HasContent() + /// A value indicating whether to consider unpublished content. + /// A value indicating whether the underlying non-contextual cache contains content. + public override bool HasContent(bool preview) { - return _cache.HasContent(UmbracoContext); + return _cache.HasContent(UmbracoContext, preview); } } } diff --git a/src/Umbraco.Web/PublishedCache/ContextualPublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/ContextualPublishedContentCache.cs index 728f114cce..41e540afe5 100644 --- a/src/Umbraco.Web/PublishedCache/ContextualPublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/ContextualPublishedContentCache.cs @@ -26,10 +26,26 @@ namespace Umbraco.Web.PublishedCache /// The route /// 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. + /// + /// 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. + /// Considers published or unpublished content depending on context. + /// public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null) { - return InnerCache.GetByRoute(UmbracoContext, route, hideTopLevelNode); + return GetByRoute(UmbracoContext.InPreviewMode, route, hideTopLevelNode); + } + + /// + /// Gets content identified by a route. + /// + /// A value indicating whether to consider unpublished content. + /// The route + /// 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(bool preview, string route, bool? hideTopLevelNode = null) + { + return InnerCache.GetByRoute(UmbracoContext, preview, route, hideTopLevelNode); } /// @@ -37,9 +53,22 @@ namespace Umbraco.Web.PublishedCache /// /// The content unique identifier. /// The route. + /// Considers published or unpublished content depending on context. public string GetRouteById(int contentId) { - return InnerCache.GetRouteById(UmbracoContext, contentId); + return GetRouteById(UmbracoContext.InPreviewMode, contentId); + } + + /// + /// Gets the route for a content identified by its unique identifier. + /// + /// A value indicating whether to consider unpublished content. + /// The content unique identifier. + /// The route. + /// Considers published or unpublished content depending on context. + public string GetRouteById(bool preview, int contentId) + { + return InnerCache.GetRouteById(UmbracoContext, preview, contentId); } } } diff --git a/src/Umbraco.Web/PublishedCache/IPublishedCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedCache.cs index 4e48e192eb..fb9d7e71b9 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedCache.cs @@ -20,49 +20,62 @@ namespace Umbraco.Web.PublishedCache /// Gets a content identified by its unique identifier. /// /// The context. + /// A value indicating whether to consider unpublished content. /// The content unique identifier. /// The content, or null. - IPublishedContent GetById(UmbracoContext umbracoContext, int contentId); + /// The value of overrides the context. + IPublishedContent GetById(UmbracoContext umbracoContext, bool preview, int contentId); /// /// Gets contents at root. /// /// The context. + /// A value indicating whether to consider unpublished content. /// The contents. - IEnumerable GetAtRoot(UmbracoContext umbracoContext); + /// The value of overrides the context. + IEnumerable GetAtRoot(UmbracoContext umbracoContext, bool preview); /// /// Gets a content resulting from an XPath query. /// /// The context. + /// A value indicating whether to consider unpublished content. /// The XPath query. /// Optional XPath variables. /// The content, or null. - IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, string xpath, XPathVariable[] vars); + /// The value of overrides the context. + IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, string xpath, XPathVariable[] vars); /// /// Gets contents resulting from an XPath query. /// /// The context. + /// A value indicating whether to consider unpublished content. /// The XPath query. /// Optional XPath variables. /// The contents. - IEnumerable GetByXPath(UmbracoContext umbracoContext, string xpath, XPathVariable[] vars); + /// The value of overrides the context. + IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, string xpath, XPathVariable[] vars); /// /// Gets an XPath navigator that can be used to navigate contents. /// /// The context. + /// A value indicating whether to consider unpublished content. /// The XPath navigator. - XPathNavigator GetXPathNavigator(UmbracoContext umbracoContext); + /// The value of overrides the context. + XPathNavigator GetXPathNavigator(UmbracoContext umbracoContext, bool preview); /// /// Gets a value indicating whether the cache contains published content. /// + /// The context. + /// A value indicating whether to consider unpublished content. /// A value indicating whether the cache contains published content. - bool HasContent(); - - //TODO: SD: We should make this happen! This will allow us to natively do a GetByDocumentType query + /// The value of overrides the context. + bool HasContent(UmbracoContext umbracoContext, bool preview); + + //TODO: SD: We should make this happen! This will allow us to natively do a GetByDocumentType query // on the UmbracoHelper (or an internal DataContext that it uses, etc...) // One issue is that we need to make media work as fast as we can and need to create a ConvertFromMediaObject // method in the DefaultPublishedMediaStore, there's already a TODO noting this but in order to do that we'll diff --git a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs index 3745271e43..c892af507b 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs @@ -13,21 +13,25 @@ namespace Umbraco.Web.PublishedCache /// Gets content identified by a route. /// /// The context. + /// A value indicating whether to consider unpublished content. /// The route /// 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. /// If is null then the settings value is used. + /// The value of overrides the context. /// - IPublishedContent GetByRoute(UmbracoContext umbracoContext, string route, bool? hideTopLevelNode = null); + IPublishedContent GetByRoute(UmbracoContext umbracoContext, bool preview, string route, bool? hideTopLevelNode = null); /// /// Gets the route for a content identified by its unique identifier. /// /// The context. + /// A value indicating whether to consider unpublished content. /// The content unique identifier. /// The route. - string GetRouteById(UmbracoContext umbracoContext, int contentId); + /// The value of overrides the context. + string GetRouteById(UmbracoContext umbracoContext, bool preview, int contentId); } } diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs index 915c4fb55f..10bedb99cb 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs @@ -27,40 +27,29 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // for INTERNAL, UNIT TESTS use ONLY internal static bool UnitTesting = false; - /// - /// Gets content identified by a route. - /// - /// The context. - /// The route - /// 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. - /// If is null then the settings value is used. - /// - public virtual IPublishedContent GetByRoute(UmbracoContext umbracoContext, string route, bool? hideTopLevelNode = null) + public virtual IPublishedContent GetByRoute(UmbracoContext umbracoContext, bool preview, string route, bool? hideTopLevelNode = null) { if (route == null) throw new ArgumentNullException("route"); // try to get from cache if not previewing - var contentId = umbracoContext.InPreviewMode ? 0 : _routesCache.GetNodeId(route); + var contentId = preview ? 0 : _routesCache.GetNodeId(route); // if found id in cache then get corresponding content // and clear cache if not found - for whatever reason IPublishedContent content = null; if (contentId > 0) { - content = GetById(umbracoContext, contentId); + content = GetById(umbracoContext, preview, contentId); if (content == null) _routesCache.ClearNode(contentId); } // still have nothing? actually determine the id hideTopLevelNode = hideTopLevelNode ?? GlobalSettings.HideTopLevelNodeFromPath; // default = settings - content = content ?? DetermineIdByRoute(umbracoContext, route, hideTopLevelNode.Value); + content = content ?? DetermineIdByRoute(umbracoContext, preview, route, hideTopLevelNode.Value); // cache if we have a content and not previewing - if (content != null && !umbracoContext.InPreviewMode) + if (content != null && !preview) { var domainRootNodeId = route.StartsWith("/") ? -1 : int.Parse(route.Substring(0, route.IndexOf('/'))); var iscanon = !UnitTesting && !DomainHelper.ExistsDomainInPath(DomainHelper.GetAllDomains(false), content.Path, domainRootNodeId); @@ -72,32 +61,26 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return content; } - /// - /// Gets the route for a content identified by its unique identifier. - /// - /// The context. - /// The content unique identifier. - /// The route. - public virtual string GetRouteById(UmbracoContext umbracoContext, int contentId) + public virtual string GetRouteById(UmbracoContext umbracoContext, bool preview, int contentId) { // try to get from cache if not previewing - var route = umbracoContext.InPreviewMode ? null : _routesCache.GetRoute(contentId); + var route = preview ? null : _routesCache.GetRoute(contentId); // if found in cache then return if (route != null) return route; // else actually determine the route - route = DetermineRouteById(umbracoContext, contentId); + route = DetermineRouteById(umbracoContext, preview, contentId); // cache if we have a route and not previewing - if (route != null && !umbracoContext.InPreviewMode) + if (route != null && !preview) _routesCache.Store(contentId, route); return route; } - IPublishedContent DetermineIdByRoute(UmbracoContext umbracoContext, string route, bool hideTopLevelNode) + IPublishedContent DetermineIdByRoute(UmbracoContext umbracoContext, bool preview, string route, bool hideTopLevelNode) { if (route == null) throw new ArgumentNullException("route"); @@ -112,7 +95,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache var xpath = CreateXpathQuery(startNodeId, path, hideTopLevelNode, out vars); //check if we can find the node in our xml cache - var content = GetSingleByXPath(umbracoContext, xpath, vars == null ? null : vars.ToArray()); + var content = GetSingleByXPath(umbracoContext, preview, xpath, vars == null ? null : vars.ToArray()); // 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 @@ -120,15 +103,15 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache if (content == null && hideTopLevelNode && path.Length > 1 && path.IndexOf('/', 1) < 0) { xpath = CreateXpathQuery(startNodeId, path, false, out vars); - content = GetSingleByXPath(umbracoContext, xpath, vars == null ? null : vars.ToArray()); + content = GetSingleByXPath(umbracoContext, preview, xpath, vars == null ? null : vars.ToArray()); } return content; } - string DetermineRouteById(UmbracoContext umbracoContext, int contentId) + string DetermineRouteById(UmbracoContext umbracoContext, bool preview, int contentId) { - var node = GetById(umbracoContext, contentId); + var node = GetById(umbracoContext, preview, contentId); if (node == null) return null; @@ -264,52 +247,52 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache #region Getters - public virtual IPublishedContent GetById(UmbracoContext umbracoContext, int nodeId) + public virtual IPublishedContent GetById(UmbracoContext umbracoContext, bool preview, int nodeId) { - return ConvertToDocument(GetXml(umbracoContext).GetElementById(nodeId.ToString())); + return ConvertToDocument(GetXml(umbracoContext, preview).GetElementById(nodeId.ToString(CultureInfo.InvariantCulture))); } - public virtual IEnumerable GetAtRoot(UmbracoContext umbracoContext) - { - return (from XmlNode x in GetXml(umbracoContext).SelectNodes(XPathStrings.RootDocuments) select ConvertToDocument(x)).ToList(); + public virtual IEnumerable GetAtRoot(UmbracoContext umbracoContext, bool preview) + { + return ConvertToDocuments(GetXml(umbracoContext, preview).SelectNodes(XPathStrings.RootDocuments)); } - public virtual IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, string xpath, params XPathVariable[] vars) + public virtual IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, string xpath, params XPathVariable[] vars) { if (xpath == null) throw new ArgumentNullException("xpath"); if (string.IsNullOrWhiteSpace(xpath)) return null; - var xml = GetXml(umbracoContext); + var xml = GetXml(umbracoContext, preview); var node = vars == null ? xml.SelectSingleNode(xpath) : xml.SelectSingleNode(xpath, vars); return ConvertToDocument(node); } - public virtual IEnumerable GetByXPath(UmbracoContext umbracoContext, string xpath, params XPathVariable[] vars) + public virtual IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, string xpath, params XPathVariable[] vars) { if (xpath == null) throw new ArgumentNullException("xpath"); if (string.IsNullOrWhiteSpace(xpath)) return Enumerable.Empty(); - var xml = GetXml(umbracoContext); + var xml = GetXml(umbracoContext, preview); var nodes = vars == null ? xml.SelectNodes(xpath) : xml.SelectNodes(xpath, vars); return ConvertToDocuments(nodes); } - public virtual bool HasContent() + public virtual bool HasContent(UmbracoContext umbracoContext, bool preview) { - var xml = GetXml(); + var xml = GetXml(umbracoContext, preview); if (xml == null) return false; var node = xml.SelectSingleNode(XPathStrings.RootDocuments); return node != null; } - public virtual XPathNavigator GetXPathNavigator(UmbracoContext umbracoContext) + public virtual XPathNavigator GetXPathNavigator(UmbracoContext umbracoContext, bool preview) { - var xml = GetXml(umbracoContext); + var xml = GetXml(umbracoContext, preview); return xml.CreateNavigator(); } @@ -355,14 +338,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache } } - internal XmlDocument GetXml(UmbracoContext umbracoContext) + internal XmlDocument GetXml(UmbracoContext umbracoContext, bool preview) { - return GetXmlDelegate(umbracoContext.UmbracoUser, umbracoContext.InPreviewMode); - } - - private XmlDocument GetXml() - { - return GetXmlDelegate(null, false); + return GetXmlDelegate(umbracoContext.UmbracoUser, preview); } #endregion diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index 08e0f65a2f..debb1f8a22 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -47,12 +47,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache private readonly BaseSearchProvider _searchProvider; private readonly BaseIndexProvider _indexProvider; - public virtual IPublishedContent GetById(UmbracoContext umbracoContext, int nodeId) + public virtual IPublishedContent GetById(UmbracoContext umbracoContext, bool preview, int nodeId) { return GetUmbracoMedia(nodeId); } - public IEnumerable GetAtRoot(UmbracoContext umbracoContext) + public virtual IEnumerable GetAtRoot(UmbracoContext umbracoContext, bool preview) { var rootMedia = global::umbraco.cms.businesslogic.media.Media.GetRootMedias(); var result = new List(); @@ -67,22 +67,22 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return result; } - public IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, string xpath, XPathVariable[] vars) + public virtual IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, string xpath, XPathVariable[] vars) { throw new NotImplementedException("PublishedMediaCache does not support XPath."); } - public IEnumerable GetByXPath(UmbracoContext umbracoContext, string xpath, XPathVariable[] vars) + public virtual IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, string xpath, XPathVariable[] vars) { throw new NotImplementedException("PublishedMediaCache does not support XPath."); } - public virtual XPathNavigator GetXPathNavigator(UmbracoContext umbracoContext) + public virtual XPathNavigator GetXPathNavigator(UmbracoContext umbracoContext, bool preview) { throw new NotImplementedException("PublishedMediaCache does not support XPath."); } - public bool HasContent() { throw new NotImplementedException(); } + public virtual bool HasContent(UmbracoContext context, bool preview) { throw new NotImplementedException(); } private ExamineManager GetExamineManagerSafe() { diff --git a/src/Umbraco.Web/umbraco.presentation/UmbracoContext.cs b/src/Umbraco.Web/umbraco.presentation/UmbracoContext.cs index dd355562a8..b065c6829c 100644 --- a/src/Umbraco.Web/umbraco.presentation/UmbracoContext.cs +++ b/src/Umbraco.Web/umbraco.presentation/UmbracoContext.cs @@ -88,7 +88,7 @@ namespace umbraco.presentation if (cache == null) throw new InvalidOperationException("Unsupported IPublishedContentCache, only the Xml one is supported."); - return cache.GetXml(umbracoContext); + return cache.GetXml(umbracoContext, umbracoContext.InPreviewMode); } /// diff --git a/src/Umbraco.Web/umbraco.presentation/macro.cs b/src/Umbraco.Web/umbraco.presentation/macro.cs index ea31d49e87..dc0fc04f8b 100644 --- a/src/Umbraco.Web/umbraco.presentation/macro.cs +++ b/src/Umbraco.Web/umbraco.presentation/macro.cs @@ -771,7 +771,7 @@ namespace umbraco // get master xml document var cache = UmbracoContext.Current.ContentCache.InnerCache as Umbraco.Web.PublishedCache.XmlPublishedCache.PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); - XmlDocument umbracoXml = cache.GetXml(UmbracoContext.Current); + XmlDocument umbracoXml = cache.GetXml(UmbracoContext.Current, UmbracoContext.Current.InPreviewMode); macroXml = new XmlDocument(); macroXml.LoadXml(""); foreach (var prop in macro.Model.Properties) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs index e3303d2823..0116b2f781 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs @@ -106,7 +106,7 @@ namespace umbraco.presentation.templateControls //moved the following from the catch block up as this will allow fallback options alt text etc to work var cache = Umbraco.Web.UmbracoContext.Current.ContentCache.InnerCache as PublishedContentCache; if (cache == null) throw new InvalidOperationException("Unsupported IPublishedContentCache, only the Xml one is supported."); - var xml = cache.GetXml(Umbraco.Web.UmbracoContext.Current); + var xml = cache.GetXml(Umbraco.Web.UmbracoContext.Current, Umbraco.Web.UmbracoContext.Current.InPreviewMode); var itemPage = new page(xml.GetElementById(tempNodeId.ToString())); tempElementContent = new item(itemPage.Elements, item.LegacyAttributes).FieldContent; } From 7a1e3b96b82e583f526c985495eae9a7741d4930 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 4 Apr 2013 05:10:53 -0200 Subject: [PATCH 13/14] Web.PublishedCache - IPublishedCaches doesn't return !contextual caches anymore --- .../Cache/ContentTypeCacheRefresher.cs | 2 +- src/Umbraco.Web/Cache/DomainCacheRefresher.cs | 2 +- .../PublishedCache/IPublishedCaches.cs | 10 --------- .../PublishedCache/PublishedCaches.cs | 21 +++++++------------ 4 files changed, 9 insertions(+), 26 deletions(-) diff --git a/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs index 6fac44e2cb..46e8b91371 100644 --- a/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs @@ -215,7 +215,7 @@ namespace Umbraco.Web.Cache // are creating a nasty dependency - but keep it like that for the time being while // SD is cleaning cache refreshers up. - var contentCache = PublishedCachesResolver.Current.Caches.ContentCache as PublishedContentCache; + var contentCache = UmbracoContext.Current.ContentCache.InnerCache as PublishedContentCache; if (contentCache != null) contentCache.RoutesCache.Clear(); } diff --git a/src/Umbraco.Web/Cache/DomainCacheRefresher.cs b/src/Umbraco.Web/Cache/DomainCacheRefresher.cs index 27a8a70eda..3d998ae718 100644 --- a/src/Umbraco.Web/Cache/DomainCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/DomainCacheRefresher.cs @@ -48,7 +48,7 @@ namespace Umbraco.Web.Cache // are creating a nasty dependency - but keep it like that for the time being while // SD is cleaning cache refreshers up. - var contentCache = PublishedCachesResolver.Current.Caches.ContentCache as PublishedContentCache; + var contentCache = UmbracoContext.Current.ContentCache.InnerCache as PublishedContentCache; if (contentCache != null) contentCache.RoutesCache.Clear(); } diff --git a/src/Umbraco.Web/PublishedCache/IPublishedCaches.cs b/src/Umbraco.Web/PublishedCache/IPublishedCaches.cs index 3125656c79..82609b7457 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedCaches.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedCaches.cs @@ -11,16 +11,6 @@ namespace Umbraco.Web.PublishedCache /// Groups caches that _may_ be related. interface IPublishedCaches { - /// - /// Gets the content cache. - /// - IPublishedContentCache ContentCache { get; } - - /// - /// Gets the media cache. - /// - IPublishedMediaCache MediaCache { get; } - /// /// Creates a contextual content cache for a specified context. /// diff --git a/src/Umbraco.Web/PublishedCache/PublishedCaches.cs b/src/Umbraco.Web/PublishedCache/PublishedCaches.cs index 7aa7e2f501..287cd740e6 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedCaches.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedCaches.cs @@ -6,26 +6,19 @@ /// Default implementation for unrelated caches. class PublishedCaches : IPublishedCaches { + private readonly IPublishedContentCache _contentCache; + private readonly IPublishedMediaCache _mediaCache; + /// /// Initializes a new instance of the class with a content cache /// and a media cache. /// public PublishedCaches(IPublishedContentCache contentCache, IPublishedMediaCache mediaCache) { - ContentCache = contentCache; - MediaCache = mediaCache; + _contentCache = contentCache; + _mediaCache = mediaCache; } - /// - /// Gets the content cache. - /// - public IPublishedContentCache ContentCache { get; private set; } - - /// - /// Gets the media cache. - /// - public IPublishedMediaCache MediaCache { get; private set; } - /// /// Creates a contextual content cache for a specified context. /// @@ -33,7 +26,7 @@ /// A new contextual content cache for the specified context. public ContextualPublishedContentCache CreateContextualContentCache(UmbracoContext context) { - return new ContextualPublishedContentCache(ContentCache, context); + return new ContextualPublishedContentCache(_contentCache, context); } /// @@ -43,7 +36,7 @@ /// A new contextual media cache for the specified context. public ContextualPublishedMediaCache CreateContextualMediaCache(UmbracoContext context) { - return new ContextualPublishedMediaCache(MediaCache, context); + return new ContextualPublishedMediaCache(_mediaCache, context); } } } From 106d733d08957e74ebeee5b59a7c5e7c0ddfe15a Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 10 Apr 2013 12:49:45 -0200 Subject: [PATCH 14/14] Web.PublishedCache - support XPathExpression --- .../ContextualPublishedCache.cs | 62 +++++++++++++++++++ .../ContextualPublishedCacheOfT.cs | 34 ++++++++++ .../PublishedCache/IPublishedCache.cs | 22 +++++++ .../PublishedContentCache.cs | 22 +++++++ .../XmlPublishedCache/PublishedMediaCache.cs | 12 +++- src/Umbraco.Web/UmbracoHelper.cs | 51 +++++++++++++-- 6 files changed, 198 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs b/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs index 6436384ffa..188e5712fb 100644 --- a/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs +++ b/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs @@ -77,6 +77,23 @@ namespace Umbraco.Web.PublishedCache return GetSingleByXPath(UmbracoContext.InPreviewMode, xpath, vars); } + /// + /// Gets a content resulting from an XPath query. + /// + /// The XPath query. + /// Optional XPath variables. + /// The content, or null. + /// + /// 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. + /// Considers published or unpublished content depending on context. + /// + public IPublishedContent GetSingleByXPath(XPathExpression xpath, params XPathVariable[] vars) + { + return GetSingleByXPath(UmbracoContext.InPreviewMode, xpath, vars); + } + /// /// Gets a content resulting from an XPath query. /// @@ -91,6 +108,20 @@ namespace Umbraco.Web.PublishedCache /// public abstract IPublishedContent GetSingleByXPath(bool preview, string xpath, params XPathVariable[] vars); + /// + /// Gets a content resulting from an XPath query. + /// + /// A value indicating whether to consider unpublished content. + /// The XPath query. + /// Optional XPath variables. + /// The content, or null. + /// + /// 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 abstract IPublishedContent GetSingleByXPath(bool preview, XPathExpression xpath, params XPathVariable[] vars); + /// /// Gets content resulting from an XPath query. /// @@ -108,6 +139,23 @@ namespace Umbraco.Web.PublishedCache return GetByXPath(UmbracoContext.InPreviewMode, xpath, vars); } + /// + /// Gets content resulting from an XPath query. + /// + /// The XPath query. + /// Optional XPath variables. + /// The contents. + /// + /// 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. + /// Considers published or unpublished content depending on context. + /// + public IEnumerable GetByXPath(XPathExpression xpath, params XPathVariable[] vars) + { + return GetByXPath(UmbracoContext.InPreviewMode, xpath, vars); + } + /// /// Gets content resulting from an XPath query. /// @@ -122,6 +170,20 @@ namespace Umbraco.Web.PublishedCache /// public abstract IEnumerable GetByXPath(bool preview, string xpath, params XPathVariable[] vars); + /// + /// Gets content resulting from an XPath query. + /// + /// A value indicating whether to consider unpublished content. + /// The XPath query. + /// Optional XPath variables. + /// The contents. + /// + /// 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 abstract IEnumerable GetByXPath(bool preview, XPathExpression xpath, params XPathVariable[] vars); + /// /// Gets an XPath navigator that can be used to navigate content. /// diff --git a/src/Umbraco.Web/PublishedCache/ContextualPublishedCacheOfT.cs b/src/Umbraco.Web/PublishedCache/ContextualPublishedCacheOfT.cs index a279026e86..67030fa50d 100644 --- a/src/Umbraco.Web/PublishedCache/ContextualPublishedCacheOfT.cs +++ b/src/Umbraco.Web/PublishedCache/ContextualPublishedCacheOfT.cs @@ -71,6 +71,23 @@ namespace Umbraco.Web.PublishedCache return _cache.GetSingleByXPath(UmbracoContext, preview, xpath, vars); } + /// + /// Gets a content resulting from an XPath query. + /// + /// A value indicating whether to consider unpublished content. + /// The XPath query. + /// Optional XPath variables. + /// The content, or null. + /// + /// 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 override IPublishedContent GetSingleByXPath(bool preview, XPathExpression xpath, params XPathVariable[] vars) + { + return _cache.GetSingleByXPath(UmbracoContext, preview, xpath, vars); + } + /// /// Gets content resulting from an XPath query. /// @@ -88,6 +105,23 @@ namespace Umbraco.Web.PublishedCache return _cache.GetByXPath(UmbracoContext, preview, xpath, vars); } + /// + /// Gets content resulting from an XPath query. + /// + /// A value indicating whether to consider unpublished content. + /// The XPath query. + /// Optional XPath variables. + /// The contents. + /// + /// 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 override IEnumerable GetByXPath(bool preview, XPathExpression xpath, params XPathVariable[] vars) + { + return _cache.GetByXPath(UmbracoContext, preview, xpath, vars); + } + /// /// Gets an XPath navigator that can be used to navigate content. /// diff --git a/src/Umbraco.Web/PublishedCache/IPublishedCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedCache.cs index fb9d7e71b9..50a161422e 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedCache.cs @@ -46,6 +46,17 @@ namespace Umbraco.Web.PublishedCache /// The value of overrides the context. IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, string xpath, XPathVariable[] vars); + /// + /// Gets a content resulting from an XPath query. + /// + /// The context. + /// A value indicating whether to consider unpublished content. + /// The XPath query. + /// Optional XPath variables. + /// The content, or null. + /// The value of overrides the context. + IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, XPathExpression xpath, XPathVariable[] vars); + /// /// Gets contents resulting from an XPath query. /// @@ -57,6 +68,17 @@ namespace Umbraco.Web.PublishedCache /// The value of overrides the context. IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, string xpath, XPathVariable[] vars); + /// + /// Gets contents resulting from an XPath query. + /// + /// The context. + /// A value indicating whether to consider unpublished content. + /// The XPath query. + /// Optional XPath variables. + /// The contents. + /// The value of overrides the context. + IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, XPathExpression xpath, XPathVariable[] vars); + /// /// Gets an XPath navigator that can be used to navigate contents. /// diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs index 10bedb99cb..15372a1758 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs @@ -269,6 +269,17 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return ConvertToDocument(node); } + public virtual IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, XPathExpression xpath, params XPathVariable[] vars) + { + if (xpath == null) throw new ArgumentNullException("xpath"); + + var xml = GetXml(umbracoContext, preview); + var node = vars == null + ? xml.SelectSingleNode(xpath) + : xml.SelectSingleNode(xpath, vars); + return ConvertToDocument(node); + } + public virtual IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, string xpath, params XPathVariable[] vars) { if (xpath == null) throw new ArgumentNullException("xpath"); @@ -281,6 +292,17 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return ConvertToDocuments(nodes); } + public virtual IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, XPathExpression xpath, params XPathVariable[] vars) + { + if (xpath == null) throw new ArgumentNullException("xpath"); + + var xml = GetXml(umbracoContext, preview); + var nodes = vars == null + ? xml.SelectNodes(xpath) + : xml.SelectNodes(xpath, vars); + return ConvertToDocuments(nodes); + } + public virtual bool HasContent(UmbracoContext umbracoContext, bool preview) { var xml = GetXml(umbracoContext, preview); diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index debb1f8a22..b42aae843b 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -72,7 +72,17 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache throw new NotImplementedException("PublishedMediaCache does not support XPath."); } - public virtual IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, string xpath, XPathVariable[] vars) + public virtual IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, XPathExpression xpath, XPathVariable[] vars) + { + throw new NotImplementedException("PublishedMediaCache does not support XPath."); + } + + public virtual IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, string xpath, XPathVariable[] vars) + { + throw new NotImplementedException("PublishedMediaCache does not support XPath."); + } + + public virtual IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, XPathExpression xpath, XPathVariable[] vars) { throw new NotImplementedException("PublishedMediaCache does not support XPath."); } diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 17fb53ddfa..2e8f4808d6 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -492,6 +492,11 @@ namespace Umbraco.Web return TypedDocumentsByXPath(xpath, vars, _umbracoContext.ContentCache); } + public IEnumerable TypedContentAtXPath(XPathExpression xpath, params XPathVariable[] vars) + { + return TypedDocumentsByXPath(xpath, vars, _umbracoContext.ContentCache); + } + public IEnumerable TypedContentAtRoot() { return TypedDocumentsAtRoot(_umbracoContext.ContentCache); @@ -517,7 +522,12 @@ namespace Umbraco.Web return DocumentByXPath(xpath, vars, _umbracoContext.ContentCache, new DynamicNull()); } - public dynamic Content(params object[] ids) + public dynamic ContentSingleAtXPath(XPathExpression xpath, params XPathVariable[] vars) + { + return DocumentByXPath(xpath, vars, _umbracoContext.ContentCache, new DynamicNull()); + } + + public dynamic Content(params object[] ids) { return DocumentByIds(_umbracoContext.ContentCache, ids); } @@ -552,6 +562,11 @@ namespace Umbraco.Web return DocumentsByXPath(xpath, vars, _umbracoContext.ContentCache); } + public dynamic ContentAtXPath(XPathExpression xpath, params XPathVariable[] vars) + { + return DocumentsByXPath(xpath, vars, _umbracoContext.ContentCache); + } + public dynamic ContentAtRoot() { return DocumentsAtRoot(_umbracoContext.ContentCache); @@ -715,7 +730,13 @@ namespace Umbraco.Web return doc; } - /// + private IPublishedContent TypedDocumentByXPath(XPathExpression xpath, XPathVariable[] vars, ContextualPublishedContentCache cache) + { + var doc = cache.GetSingleByXPath(xpath, vars); + return doc; + } + + /// /// Overloaded method accepting an 'object' type /// /// @@ -747,6 +768,12 @@ namespace Umbraco.Web return doc; } + private IEnumerable TypedDocumentsByXPath(XPathExpression xpath, XPathVariable[] vars, ContextualPublishedContentCache cache) + { + var doc = cache.GetByXPath(xpath, vars); + return doc; + } + private IEnumerable TypedDocumentsAtRoot(ContextualPublishedCache cache) { return cache.GetAtRoot(); @@ -796,8 +823,16 @@ namespace Umbraco.Web ? ifNotFound : new DynamicPublishedContent(doc).AsDynamic(); } - - /// + + private dynamic DocumentByXPath(XPathExpression xpath, XPathVariable[] vars, ContextualPublishedCache cache, object ifNotFound) + { + var doc = cache.GetSingleByXPath(xpath, vars); + return doc == null + ? ifNotFound + : new DynamicPublishedContent(doc).AsDynamic(); + } + + /// /// Overloaded method accepting an 'object' type /// /// @@ -843,6 +878,14 @@ namespace Umbraco.Web ); } + private dynamic DocumentsByXPath(XPathExpression xpath, XPathVariable[] vars, ContextualPublishedCache cache) + { + return new DynamicPublishedContentList( + cache.GetByXPath(xpath, vars) + .Select(publishedContent => new DynamicPublishedContent(publishedContent)) + ); + } + private dynamic DocumentsAtRoot(ContextualPublishedCache cache) { return new DynamicPublishedContentList(