diff --git a/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs index 70ca81fcc4..fb54207578 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs @@ -27,7 +27,7 @@ namespace Umbraco.Tests.Routing var docRequest = new PublishedContentRequest(url, routingContext); var lookup = new ContentFinderByUrlAlias(); - var result = lookup.TryFindDocument(docRequest); + var result = lookup.TryFindContent(docRequest); Assert.IsTrue(result); Assert.AreEqual(docRequest.PublishedContent.Id, nodeMatch); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs index 95215333b8..0636b3828f 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs @@ -76,7 +76,7 @@ namespace Umbraco.Tests.Routing Assert.AreEqual(expectedCulture, pcr.Culture.Name); var finder = new ContentFinderByUrlAlias(); - var result = finder.TryFindDocument(pcr); + var result = finder.TryFindContent(pcr); if (expectedNode > 0) { diff --git a/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs index 14bf23d0a2..dcbf1dcb9b 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs @@ -27,7 +27,7 @@ namespace Umbraco.Tests.Routing var lookup = new ContentFinderByIdPath(); - var result = lookup.TryFindDocument(docRequest); + var result = lookup.TryFindContent(docRequest); Assert.IsTrue(result); Assert.AreEqual(docRequest.PublishedContent.Id, nodeMatch); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlAndTemplateTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlAndTemplateTests.cs index e9a9a74135..7c4618f0b4 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlAndTemplateTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlAndTemplateTests.cs @@ -33,7 +33,7 @@ namespace Umbraco.Tests.Routing SettingsForTests.HideTopLevelNodeFromPath = false; - var result = lookup.TryFindDocument(docRequest); + var result = lookup.TryFindContent(docRequest); Assert.IsTrue(result); Assert.IsNotNull(docRequest.PublishedContent); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlTests.cs index bee591e6a1..2b10125af9 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlTests.cs @@ -1,6 +1,8 @@ +using System; using System.Configuration; using NUnit.Framework; using Umbraco.Tests.TestHelpers; +using Umbraco.Web.PublishedCache.LegacyXmlCache; using Umbraco.Web.Routing; using umbraco.BusinessLogic; using umbraco.cms.businesslogic.template; @@ -34,10 +36,10 @@ namespace Umbraco.Tests.Routing var routingContext = GetRoutingContext(urlString); var url = routingContext.UmbracoContext.CleanedUmbracoUrl; //very important to use the cleaned up umbraco url var docreq = new PublishedContentRequest(url, routingContext); - var lookup = new ContentFinderByNiceUrl(false); + var lookup = new ContentFinderByNiceUrl(); SettingsForTests.HideTopLevelNodeFromPath = true; - var result = lookup.TryFindDocument(docreq); + var result = lookup.TryFindContent(docreq); if (expectedId > 0) { @@ -61,10 +63,10 @@ namespace Umbraco.Tests.Routing var routingContext = GetRoutingContext(urlString); var url = routingContext.UmbracoContext.CleanedUmbracoUrl; //very important to use the cleaned up umbraco url var docreq = new PublishedContentRequest(url, routingContext); - var lookup = new ContentFinderByNiceUrl(false); + var lookup = new ContentFinderByNiceUrl(); SettingsForTests.HideTopLevelNodeFromPath = false; - var result = lookup.TryFindDocument(docreq); + var result = lookup.TryFindContent(docreq); Assert.IsTrue(result); Assert.AreEqual(expectedId, docreq.PublishedContent.Id); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlWithDomainsTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlWithDomainsTests.cs index 633796f9d5..2764787bcd 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlWithDomainsTests.cs @@ -160,7 +160,7 @@ namespace Umbraco.Tests.Routing pcr.Engine.FindDomain(); var lookup = new ContentFinderByNiceUrl(); - var result = lookup.TryFindDocument(pcr); + var result = lookup.TryFindContent(pcr); Assert.IsTrue(result); Assert.AreEqual(expectedId, pcr.PublishedContent.Id); } @@ -199,7 +199,7 @@ namespace Umbraco.Tests.Routing Assert.AreEqual(expectedCulture, pcr.Culture.Name); var lookup = new ContentFinderByNiceUrl(); - var result = lookup.TryFindDocument(pcr); + var result = lookup.TryFindContent(pcr); Assert.IsTrue(result); Assert.AreEqual(expectedId, pcr.PublishedContent.Id); } diff --git a/src/Umbraco.Tests/Routing/ContentFinderByPageIdQueryTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByPageIdQueryTests.cs index 6852f72fe0..4807a02ae1 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByPageIdQueryTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByPageIdQueryTests.cs @@ -34,7 +34,7 @@ namespace Umbraco.Tests.Routing routingContext.UmbracoContext.HttpContext.Request.Stub(x => x["umbPageID"]) .Return(routingContext.UmbracoContext.HttpContext.Request.QueryString["umbPageID"]); - var result = lookup.TryFindDocument(docRequest); + var result = lookup.TryFindContent(docRequest); Assert.IsTrue(result); Assert.AreEqual(docRequest.PublishedContent.Id, nodeMatch); diff --git a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs index 0256e2b10f..23a9141e9c 100644 --- a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs +++ b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs @@ -182,7 +182,7 @@ namespace Umbraco.Tests.Routing SettingsForTests.HideTopLevelNodeFromPath = false; var finder = new ContentFinderByNiceUrl(); - var result = finder.TryFindDocument(pcr); + var result = finder.TryFindContent(pcr); Assert.IsTrue(result); Assert.AreEqual(pcr.PublishedContent.Id, expectedNode); @@ -225,7 +225,7 @@ namespace Umbraco.Tests.Routing // find document SettingsForTests.HideTopLevelNodeFromPath = false; var finder = new ContentFinderByNiceUrl(); - var result = finder.TryFindDocument(pcr); + var result = finder.TryFindContent(pcr); // apply wildcard domain pcr.Engine.HandleWildcardDomains(); diff --git a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs b/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs index 79cab7e194..a011dd7480 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using NUnit.Framework; using Umbraco.Tests.TestHelpers; +using Umbraco.Web.PublishedCache.LegacyXmlCache; using Umbraco.Web.Routing; namespace Umbraco.Tests.Routing @@ -35,11 +36,6 @@ namespace Umbraco.Tests.Routing base.FreezeResolution(); } - internal override IRoutesCache GetRoutesCache() - { - return new DefaultRoutesCache(false); - } - /// /// This checks that when we retreive a NiceUrl for multiple items that there are no issues with cache overlap /// and that they are all cached correctly. @@ -76,7 +72,9 @@ namespace Umbraco.Tests.Routing Assert.AreEqual(randomSample.Value, result); } - var cachedRoutes = ((DefaultRoutesCache)routingContext.RoutesCache).GetCachedRoutes(); + var cache = routingContext.UmbracoContext.ContentCache.InnerCache as PublishedContentCache; + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the legacy one is supported."); + var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); Assert.AreEqual(8, cachedRoutes.Count); foreach (var sample in samples) @@ -85,7 +83,7 @@ namespace Umbraco.Tests.Routing Assert.AreEqual(sample.Value, cachedRoutes[sample.Key]); } - var cachedIds = ((DefaultRoutesCache)routingContext.RoutesCache).GetCachedIds(); + var cachedIds = cache.RoutesCache.GetCachedIds(); Assert.AreEqual(8, cachedIds.Count); foreach (var sample in samples) diff --git a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs index 88f17deb46..39f30c7368 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs @@ -5,6 +5,7 @@ using System.Text; using System.Configuration; using NUnit.Framework; using Umbraco.Tests.TestHelpers; +using Umbraco.Web.PublishedCache.LegacyXmlCache; using Umbraco.Web.Routing; using umbraco.cms.businesslogic.web; using umbraco.cms.businesslogic.language; @@ -28,11 +29,6 @@ namespace Umbraco.Tests.Routing base.FreezeResolution(); } - internal override IRoutesCache GetRoutesCache() - { - return new DefaultRoutesCache(false); - } - void InitializeLanguagesAndDomains() { var domains = Domain.GetDomains(); @@ -314,10 +310,12 @@ namespace Umbraco.Tests.Routing ignore = routingContext.UrlProvider.GetUrl(100111, new Uri("http://domain2.com"), false); ignore = routingContext.UrlProvider.GetUrl(1002, new Uri("http://domain2.com"), false); - var cachedRoutes = ((DefaultRoutesCache)routingContext.RoutesCache).GetCachedRoutes(); - Assert.AreEqual(7, cachedRoutes.Count); + var cache = routingContext.UmbracoContext.ContentCache.InnerCache as PublishedContentCache; + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the legacy one is supported."); + var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); + Assert.AreEqual(7, cachedRoutes.Count); - var cachedIds = ((DefaultRoutesCache)routingContext.RoutesCache).GetCachedIds(); + var cachedIds = cache.RoutesCache.GetCachedIds(); Assert.AreEqual(7, cachedIds.Count); CheckRoute(cachedRoutes, cachedIds, 1001, "1001/"); diff --git a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs index a329e0d4d3..b2dd369f72 100644 --- a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using NUnit.Framework; using Umbraco.Tests.TestHelpers; +using Umbraco.Web.PublishedCache.LegacyXmlCache; using umbraco.cms.businesslogic.web; using umbraco.cms.businesslogic.language; using Umbraco.Web.Routing; @@ -37,7 +38,9 @@ namespace Umbraco.Tests.Routing Assert.AreEqual("http://domain2.com/1001-1-1/", routingContext.UrlProvider.GetUrl(100111, true)); // check that the proper route has been cached - var cachedRoutes = ((DefaultRoutesCache)routingContext.RoutesCache).GetCachedRoutes(); + var cache = routingContext.UmbracoContext.ContentCache.InnerCache as PublishedContentCache; + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the legacy one is supported."); + var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); Assert.AreEqual("10011/1001-1-1", cachedRoutes[100111]); // route a rogue url @@ -49,12 +52,12 @@ namespace Umbraco.Tests.Routing // check that it's been routed var lookup = new ContentFinderByNiceUrl(); - var result = lookup.TryFindDocument(pcr); + var result = lookup.TryFindContent(pcr); Assert.IsTrue(result); Assert.AreEqual(100111, pcr.PublishedContent.Id); // has the cache been polluted? - cachedRoutes = ((DefaultRoutesCache)routingContext.RoutesCache).GetCachedRoutes(); + cachedRoutes = cache.RoutesCache.GetCachedRoutes(); Assert.AreEqual("10011/1001-1-1", cachedRoutes[100111]); // no //Assert.AreEqual("1001/1001-1/1001-1-1", cachedRoutes[100111]); // yes @@ -77,12 +80,6 @@ namespace Umbraco.Tests.Routing base.FreezeResolution(); } - internal override IRoutesCache GetRoutesCache() - { - return new DefaultRoutesCache(false); - } - - void InitializeLanguagesAndDomains() { var domains = Domain.GetDomains(); diff --git a/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs b/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs index b667ae332f..b3794fff57 100644 --- a/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs +++ b/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs @@ -17,11 +17,6 @@ namespace Umbraco.Tests.Routing [TestFixture] public class uQueryGetNodeIdByUrlTests : BaseRoutingTest { - internal override IRoutesCache GetRoutesCache() - { - return new DefaultRoutesCache(false); - } - public override void Initialize() { base.Initialize(); @@ -39,8 +34,7 @@ namespace Umbraco.Tests.Routing umbracoContext, lookups, new FakeLastChanceFinder(), - urlProvider, - GetRoutesCache()); + urlProvider); //assign the routing context back to the umbraco context umbracoContext.RoutingContext = routingContext; diff --git a/src/Umbraco.Tests/Stubs/FakeLastChanceFinder.cs b/src/Umbraco.Tests/Stubs/FakeLastChanceFinder.cs index bf8991586a..9090909baa 100644 --- a/src/Umbraco.Tests/Stubs/FakeLastChanceFinder.cs +++ b/src/Umbraco.Tests/Stubs/FakeLastChanceFinder.cs @@ -4,7 +4,7 @@ namespace Umbraco.Tests.Stubs { internal class FakeLastChanceFinder : IContentFinder { - public bool TryFindDocument(PublishedContentRequest docRequest) + public bool TryFindContent(PublishedContentRequest docRequest) { return false; } diff --git a/src/Umbraco.Tests/Stubs/FakeRoutesCache.cs b/src/Umbraco.Tests/Stubs/FakeRoutesCache.cs deleted file mode 100644 index 63c74e107a..0000000000 --- a/src/Umbraco.Tests/Stubs/FakeRoutesCache.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Umbraco.Web.Routing; - -namespace Umbraco.Tests.Stubs -{ - /// - /// Used for testing, does not cache anything - /// - public class FakeRoutesCache : IRoutesCache - { - public void Store(int nodeId, string route) - { - - } - - public string GetRoute(int nodeId) - { - return null; //default; - } - - public int GetNodeId(string route) - { - return 0; //default; - } - - public void ClearNode(int nodeId) - { - - } - - public void Clear() - { - - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index 4f04076b63..29f9c9bf10 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -288,6 +288,8 @@ namespace Umbraco.Tests.TestHelpers return doc; }; + PublishedContentCache.UnitTesting = true; + var ctx = new UmbracoContext( GetHttpContextFactory(url, routeData).HttpContext, ApplicationContext, @@ -310,11 +312,6 @@ namespace Umbraco.Tests.TestHelpers return factory; } - internal virtual IRoutesCache GetRoutesCache() - { - return new FakeRoutesCache(); - } - protected virtual string GetXmlContent(int templateId) { return @" diff --git a/src/Umbraco.Tests/TestHelpers/BaseRoutingTest.cs b/src/Umbraco.Tests/TestHelpers/BaseRoutingTest.cs index 7eb02a9d74..886b4f07bd 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseRoutingTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseRoutingTest.cs @@ -33,8 +33,7 @@ namespace Umbraco.Tests.TestHelpers umbracoContext, Enumerable.Empty(), new FakeLastChanceFinder(), - urlProvider, - GetRoutesCache()); + urlProvider); //assign the routing context back to the umbraco context umbracoContext.RoutingContext = routingContext; diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index f42fc7b225..934e56a5e0 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -347,7 +347,6 @@ - diff --git a/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs b/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs index 9b15b975da..d5da24106d 100644 --- a/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs +++ b/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs @@ -12,10 +12,12 @@ namespace Umbraco.Web.PublishedCache internal abstract class ContextualPublishedCache { protected readonly UmbracoContext UmbracoContext; + private readonly IPublishedCache _cache; - protected ContextualPublishedCache(UmbracoContext umbracoContext) + protected ContextualPublishedCache(UmbracoContext umbracoContext, IPublishedCache cache) { UmbracoContext = umbracoContext; + _cache = cache; } /// @@ -23,13 +25,19 @@ namespace Umbraco.Web.PublishedCache /// /// The content unique identifier. /// The content, or null. - public abstract IPublishedContent GetById(int contentId); + public virtual IPublishedContent GetById(int contentId) + { + return _cache.GetById(UmbracoContext, contentId); + } /// /// Gets contents at root. /// /// The contents. - public abstract IEnumerable GetAtRoot(); + public virtual IEnumerable GetAtRoot() + { + return _cache.GetAtRoot(UmbracoContext); + } /// /// Gets a content resulting from an XPath query. @@ -37,7 +45,10 @@ namespace Umbraco.Web.PublishedCache /// The XPath query. /// Optional XPath variables. /// The content, or null. - public abstract IPublishedContent GetSingleByXPath(string xpath, Core.Xml.XPathVariable[] vars); + public virtual IPublishedContent GetSingleByXPath(string xpath, Core.Xml.XPathVariable[] vars) + { + return _cache.GetSingleByXPath(UmbracoContext, xpath, vars); + } /// /// Gets contents resulting from an XPath query. @@ -45,6 +56,18 @@ namespace Umbraco.Web.PublishedCache /// The XPath query. /// Optional XPath variables. /// The contents. - public abstract IEnumerable GetByXPath(string xpath, Core.Xml.XPathVariable[] vars); + public virtual IEnumerable GetByXPath(string xpath, Core.Xml.XPathVariable[] vars) + { + return _cache.GetByXPath(UmbracoContext, xpath, vars); + } + + /// + /// 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(); + } } } diff --git a/src/Umbraco.Web/PublishedCache/ContextualPublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/ContextualPublishedContentCache.cs index 393eb1e44f..03a7632512 100644 --- a/src/Umbraco.Web/PublishedCache/ContextualPublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/ContextualPublishedContentCache.cs @@ -19,7 +19,7 @@ namespace Umbraco.Web.PublishedCache /// A published content cache. /// A context. public ContextualPublishedContentCache(IPublishedContentCache cache, UmbracoContext umbracoContext) - : base(umbracoContext) + : base(umbracoContext, cache) { _cache = cache; } @@ -31,65 +31,31 @@ namespace Umbraco.Web.PublishedCache internal IPublishedContentCache InnerCache { get { return _cache; } } /// - /// Gets a content identified by its unique identifier. + /// Gets content identified by a route. /// - /// The content unique identifier. + /// The route + /// FIXME /// 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. - public override IPublishedContent GetSingleByXPath(string xpath, Core.Xml.XPathVariable[] vars) - { - return _cache.GetSingleByXPath(UmbracoContext, xpath, vars); - } - - /// - /// Gets contents resulting from an XPath query. - /// - /// The XPath query. - /// Optional XPath variables. - /// The contents. - public override IEnumerable GetByXPath(string xpath, Core.Xml.XPathVariable[] vars) - { - return _cache.GetByXPath(UmbracoContext, xpath, vars); - } - - // FIXME do we want that one here? + /// 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); } + /// + /// Gets the route for a content identified by its unique identifier. + /// + /// The content unique identifier. + /// The route. + public string GetRouteById(int contentId) + { + return _cache.GetRouteById(UmbracoContext, contentId); + } + // FIXME do we want that one here? public IPublishedContent GetByUrlAlias(int rootNodeId, string alias) { return _cache.GetByUrlAlias(UmbracoContext, rootNodeId, alias); } - - /// - /// 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 bool HasContent() - { - return _cache.HasContent(); - } } } diff --git a/src/Umbraco.Web/PublishedCache/ContextualPublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/ContextualPublishedMediaCache.cs index 94cc37567a..c98cf5792c 100644 --- a/src/Umbraco.Web/PublishedCache/ContextualPublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/ContextualPublishedMediaCache.cs @@ -19,50 +19,9 @@ namespace Umbraco.Web.PublishedCache /// A published media cache. /// A context. public ContextualPublishedMediaCache(IPublishedMediaCache cache, UmbracoContext umbracoContext) - : base(umbracoContext) + : base(umbracoContext, cache) { _cache = 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. - public override IPublishedContent GetSingleByXPath(string xpath, Core.Xml.XPathVariable[] vars) - { - return _cache.GetSingleByXPath(UmbracoContext, xpath, vars); - } - - /// - /// Gets contents resulting from an XPath query. - /// - /// The XPath query. - /// Optional XPath variables. - /// The contents. - public override IEnumerable GetByXPath(string xpath, Core.Xml.XPathVariable[] vars) - { - return _cache.GetByXPath(UmbracoContext, xpath, vars); - } } } diff --git a/src/Umbraco.Web/PublishedCache/IPublishedCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedCache.cs index 92e78c84b0..cee82a0dbc 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedCache.cs @@ -49,8 +49,11 @@ namespace Umbraco.Web.PublishedCache /// The contents. IEnumerable GetByXPath(UmbracoContext umbracoContext, string xpath, XPathVariable[] vars); - // ... GetXPath single or multi - // ... pass the helper and NOT the store, so we're consistent?! + /// + /// Gets a value indicating whether the cache contains published 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 // on the UmbracoHelper (or an internal DataContext that it uses, etc...) diff --git a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs index 701821f159..f2898f7c68 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs @@ -9,16 +9,28 @@ namespace Umbraco.Web.PublishedCache { internal interface IPublishedContentCache : IPublishedCache { - // FIXME do we want that one? + /// + /// 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. + /// IPublishedContent GetByRoute(UmbracoContext umbracoContext, string route, bool? hideTopLevelNode = null); + /// + /// Gets the route for a content identified by its unique identifier. + /// + /// The context. + /// The content unique identifier. + /// The route. + string GetRouteById(UmbracoContext umbracoContext, int contentId); + // FIXME do we want that one? IPublishedContent GetByUrlAlias(UmbracoContext umbracoContext, int rootNodeId, string alias); - - /// - /// Gets a value indicating whether the cache contains published content. - /// - /// A value indicating whether the cache contains published content. - bool HasContent(); } } diff --git a/src/Umbraco.Web/PublishedCache/LegacyXmlCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/LegacyXmlCache/PublishedContentCache.cs index a16eef8399..43cae3f61c 100644 --- a/src/Umbraco.Web/PublishedCache/LegacyXmlCache/PublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/LegacyXmlCache/PublishedContentCache.cs @@ -1,9 +1,12 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Text; using System.Xml; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Xml; +using Umbraco.Web.Routing; using umbraco; using System.Linq; using umbraco.BusinessLogic; @@ -13,9 +16,180 @@ namespace Umbraco.Web.PublishedCache.LegacyXmlCache { internal class PublishedContentCache : IPublishedContentCache { - #region XPath Strings + #region Routes cache - class XPathStringsDefinition + private readonly RoutesCache _routesCache = new RoutesCache(!UnitTesting); + + // for INTERNAL, UNIT TESTS use ONLY + internal RoutesCache RoutesCache { get { return _routesCache; } } + + // 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 IPublishedContent GetByRoute(UmbracoContext umbracoContext, 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); + + // 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); + 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); + + // cache if we have a content and not previewing + if (content != null && !umbracoContext.InPreviewMode) + { + var domainRootNodeId = route.StartsWith("/") ? -1 : int.Parse(route.Substring(0, route.IndexOf('/'))); + var iscanon = !UnitTesting && !DomainHelper.ExistsDomainInPath(DomainHelper.GetAllDomains(false), content.Path, domainRootNodeId); + // and only if this is the canonical url (the one GetUrl would return) + if (iscanon) + _routesCache.Store(contentId, route); + } + + return content; + } + + /// + /// Gets the route for a content identified by its unique identifier. + /// + /// The context. + /// The content unique identifier. + /// The route. + public string GetRouteById(UmbracoContext umbracoContext, int contentId) + { + // try to get from cache if not previewing + var route = umbracoContext.InPreviewMode + ? null + : _routesCache.GetRoute(contentId); + + // if found in cache then return + if (route != null) + return route; + + // else actually determine the route + route = DetermineRouteById(umbracoContext, contentId); + + // cache if we have a route and not previewing + if (route != null && !umbracoContext.InPreviewMode) + _routesCache.Store(contentId, route); + + return route; + } + + IPublishedContent DetermineIdByRoute(UmbracoContext umbracoContext, string route, bool hideTopLevelNode) + { + if (route == null) throw new ArgumentNullException("route"); + + //the route always needs to be lower case because we only store the urlName attribute in lower case + route = route.ToLowerInvariant(); + + var pos = route.IndexOf('/'); + var path = pos == 0 ? route : route.Substring(pos); + var startNodeId = pos == 0 ? 0 : int.Parse(route.Substring(0, pos)); + IEnumerable vars; + + 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()); + + // if hideTopLevelNodePath is true then for url /foo we looked for /*/foo + // but maybe that was the url of a non-default top-level node, so we also + // have to look for /foo (see note in ApplyHideTopLevelNodeFromPath). + 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()); + } + + return content; + } + + string DetermineRouteById(UmbracoContext umbracoContext, int contentId) + { + var node = GetById(umbracoContext, contentId); + if (node == null) + return null; + + // walk up from that node until we hit a node with a domain, + // or we reach the content root, collecting urls in the way + var pathParts = new List(); + var n = node; + var hasDomains = DomainHelper.NodeHasDomains(n.Id); + while (!hasDomains && n != null) // n is null at root + { + // get the url + var urlName = n.UrlName; + pathParts.Add(urlName); + + // move to parent node + n = n.Parent; + hasDomains = n != null && DomainHelper.NodeHasDomains(n.Id); + } + + // no domain, respect HideTopLevelNodeFromPath for legacy purposes + if (!hasDomains && global::umbraco.GlobalSettings.HideTopLevelNodeFromPath) + ApplyHideTopLevelNodeFromPath(umbracoContext, node, pathParts); + + // assemble the route + pathParts.Reverse(); + var path = "/" + string.Join("/", pathParts); // will be "/" or "/foo" or "/foo/bar" etc + var route = (n == null ? "" : n.Id.ToString(CultureInfo.InvariantCulture)) + path; + + return route; + } + + static void ApplyHideTopLevelNodeFromPath(UmbracoContext umbracoContext, IPublishedContent node, IList pathParts) + { + // in theory if hideTopLevelNodeFromPath is true, then there should be only once + // top-level node, or else domains should be assigned. but for backward compatibility + // we add this check - we look for the document matching "/" and if it's not us, then + // we do not hide the top level path + // it has to be taken care of in GetByRoute too so if + // "/foo" fails (looking for "/*/foo") we try also "/foo". + // this does not make much sense anyway esp. if both "/foo/" and "/bar/foo" exist, but + // that's the way it works pre-4.10 and we try to be backward compat for the time being + if (node.Parent == null) + { + var rootNode = umbracoContext.ContentCache.GetByRoute("/", true); + if (rootNode.Id == node.Id) // remove only if we're the default node + pathParts.RemoveAt(pathParts.Count - 1); + } + else + { + pathParts.RemoveAt(pathParts.Count - 1); + } + } + + #endregion + + #region XPath Strings + + class XPathStringsDefinition { public int Version { get; private set; } @@ -134,39 +308,6 @@ namespace Umbraco.Web.PublishedCache.LegacyXmlCache return ConvertToDocuments(nodes); } - //FIXME keep here or remove? - public IPublishedContent GetByRoute(UmbracoContext umbracoContext, string route, bool? hideTopLevelNode = null) - { - if (route == null) throw new ArgumentNullException("route"); - - //set the default to be what is in the settings - hideTopLevelNode = hideTopLevelNode ?? GlobalSettings.HideTopLevelNodeFromPath; - - //the route always needs to be lower case because we only store the urlName attribute in lower case - route = route.ToLowerInvariant(); - - var pos = route.IndexOf('/'); - var path = pos == 0 ? route : route.Substring(pos); - var startNodeId = pos == 0 ? 0 : int.Parse(route.Substring(0, pos)); - IEnumerable vars; - - var xpath = CreateXpathQuery(startNodeId, path, hideTopLevelNode.Value, out vars); - - //check if we can find the node in our xml cache - var content = GetSingleByXPath(umbracoContext, 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 - // have to look for /foo (see note in NiceUrlProvider). - if (content == null && hideTopLevelNode.Value && path.Length > 1 && path.IndexOf('/', 1) < 0) - { - xpath = CreateXpathQuery(startNodeId, path, false, out vars); - content = GetSingleByXPath(umbracoContext, xpath, vars == null ? null : vars.ToArray()); - } - - return content; - } - // FIXME MOVE THAT ONE OUT OF HERE? public IPublishedContent GetByUrlAlias(UmbracoContext umbracoContext, int rootNodeId, string alias) { diff --git a/src/Umbraco.Web/PublishedCache/LegacyXmlCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/LegacyXmlCache/PublishedMediaCache.cs index ab5d2b231a..759e0a2a4a 100644 --- a/src/Umbraco.Web/PublishedCache/LegacyXmlCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/LegacyXmlCache/PublishedMediaCache.cs @@ -77,6 +77,8 @@ namespace Umbraco.Web.PublishedCache.LegacyXmlCache throw new NotImplementedException("PublishedMediaCache does not support XPath queries."); } + public bool HasContent() { throw new NotImplementedException(); } + private ExamineManager GetExamineManagerSafe() { try diff --git a/src/Umbraco.Web/PublishedCache/LegacyXmlCache/RoutesCache.cs b/src/Umbraco.Web/PublishedCache/LegacyXmlCache/RoutesCache.cs new file mode 100644 index 0000000000..c3d0249ea3 --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/LegacyXmlCache/RoutesCache.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Umbraco.Core.ObjectResolution; + +namespace Umbraco.Web.PublishedCache.LegacyXmlCache +{ + class RoutesCache + { + private ConcurrentDictionary _routes; + private ConcurrentDictionary _nodeIds; + + /// + /// Initializes a new instance of the class. + /// + public RoutesCache() + : this(true) + { } + + /// + /// Initializes a new instance of the class. + /// + internal RoutesCache(bool bindToEvents) + { + Clear(); + + if (bindToEvents) + { + Resolution.Frozen += ResolutionFrozen; + } + } + + /// + /// Once resolution is frozen, then we can bind to the events that we require + /// + /// + /// + private void ResolutionFrozen(object s, EventArgs args) + { + // content - whenever the entire XML cache is rebuilt (from disk cache or from database) + // we must clear the cache entirely + global::umbraco.content.AfterRefreshContent += (sender, e) => Clear(); + + // document - whenever a document is updated in, or removed from, the XML cache + // we must clear the cache - at the moment, we clear the entire cache + // TODO could we do partial updates instead of clearing the whole cache? + global::umbraco.content.AfterUpdateDocumentCache += (sender, e) => Clear(); + global::umbraco.content.AfterClearDocumentCache += (sender, e) => Clear(); + + // domains - whenever a domain change we must clear the cache + // because routes contain the id of root nodes of domains + // TODO could we do partial updates instead of clearing the whole cache? + global::umbraco.cms.businesslogic.web.Domain.AfterDelete += (sender, e) => Clear(); + global::umbraco.cms.businesslogic.web.Domain.AfterSave += (sender, e) => Clear(); + global::umbraco.cms.businesslogic.web.Domain.New += (sender, e) => Clear(); + + // FIXME + // the content class needs to be refactored - at the moment + // content.XmlContentInternal setter does not trigger any event + // content.UpdateDocumentCache(List Documents) does not trigger any event + // content.RefreshContentFromDatabaseAsync triggers AfterRefresh _while_ refreshing + // etc... + // in addition some events do not make sense... we trigger Publish when moving + // a node, which we should not (the node is moved, not published...) etc. + } + + /// + /// Used ONLY for unit tests + /// + /// + internal IDictionary GetCachedRoutes() + { + return _routes; + } + + /// + /// Used ONLY for unit tests + /// + /// + internal IDictionary GetCachedIds() + { + return _nodeIds; + } + + #region Public + + /// + /// Stores a route for a node. + /// + /// The node identified. + /// The route. + public void Store(int nodeId, string route) + { + _routes.AddOrUpdate(nodeId, i => route, (i, s) => route); + _nodeIds.AddOrUpdate(route, i => nodeId, (i, s) => nodeId); + } + + /// + /// Gets a route for a node. + /// + /// The node identifier. + /// The route for the node, else null. + public string GetRoute(int nodeId) + { + string val; + _routes.TryGetValue(nodeId, out val); + return val; + } + + /// + /// Gets a node for a route. + /// + /// The route. + /// The node identified for the route, else zero. + public int GetNodeId(string route) + { + int val; + _nodeIds.TryGetValue(route, out val); + return val; + } + + /// + /// Clears the route for a node. + /// + /// The node identifier. + public void ClearNode(int nodeId) + { + if (!_routes.ContainsKey(nodeId)) return; + + string key; + if (!_routes.TryGetValue(nodeId, out key)) return; + + int val; + _nodeIds.TryRemove(key, out val); + string val2; + _routes.TryRemove(nodeId, out val2); + } + + /// + /// Clears all routes. + /// + public void Clear() + { + _routes = new ConcurrentDictionary(); + _nodeIds = new ConcurrentDictionary(); + } + + #endregion + } +} diff --git a/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs b/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs index ecccdf097f..5de138efed 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.Routing /// /// The PublishedContentRequest. /// A value indicating whether an Umbraco document was found and assigned. - public bool TryFindDocument(PublishedContentRequest docRequest) + public bool TryFindContent(PublishedContentRequest docRequest) { IPublishedContent node = null; var path = docRequest.Uri.GetAbsolutePathDecoded(); diff --git a/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs b/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs index a42a7b6978..cf1978c323 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs @@ -13,7 +13,7 @@ namespace Umbraco.Web.Routing /// /// The PublishedContentRequest. /// A value indicating whether an Umbraco document was found and assigned. - public bool TryFindDocument(PublishedContentRequest pcr) + public bool TryFindContent(PublishedContentRequest pcr) { LogHelper.Debug("Looking for a page to handle 404."); diff --git a/src/Umbraco.Web/Routing/ContentFinderByNiceUrl.cs b/src/Umbraco.Web/Routing/ContentFinderByNiceUrl.cs index 61d362a25e..1d2e5c679a 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByNiceUrl.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByNiceUrl.cs @@ -12,28 +12,12 @@ namespace Umbraco.Web.Routing /// internal class ContentFinderByNiceUrl : IContentFinder { - private readonly bool _doDomainLookup; - - public ContentFinderByNiceUrl() - { - _doDomainLookup = true; - } - - /// - /// Contructor with flag to set whether we lookup domains in the repo to match the url or not - /// - /// - internal ContentFinderByNiceUrl(bool doDomainLookup) - { - _doDomainLookup = doDomainLookup; - } - /// /// Tries to find and assign an Umbraco document to a PublishedContentRequest. /// /// The PublishedContentRequest. /// A value indicating whether an Umbraco document was found and assigned. - public virtual bool TryFindDocument(PublishedContentRequest docRequest) + public virtual bool TryFindContent(PublishedContentRequest docRequest) { string route; if (docRequest.HasDomain) @@ -41,7 +25,7 @@ namespace Umbraco.Web.Routing else route = docRequest.Uri.GetAbsolutePathDecoded(); - var node = LookupDocumentNode(docRequest, route); + var node = FindContent(docRequest, route); return node != null; } @@ -51,62 +35,22 @@ namespace Umbraco.Web.Routing /// The document request. /// The route. /// The document node, or null. - protected IPublishedContent LookupDocumentNode(PublishedContentRequest docreq, string route) + protected IPublishedContent FindContent(PublishedContentRequest docreq, string route) { LogHelper.Debug("Test route \"{0}\"", () => route); - // first ask the cache for a node - // return '0' if in preview mode - var nodeId = !docreq.RoutingContext.UmbracoContext.InPreviewMode - ? docreq.RoutingContext.RoutesCache.GetNodeId(route) - : 0; - - // if a node was found, get it by id and ensure it exists - // else clear the cache - IPublishedContent node = null; - if (nodeId > 0) + var node = docreq.RoutingContext.UmbracoContext.ContentCache.GetByRoute(route); + if (node != null) { - node = docreq.RoutingContext.UmbracoContext.ContentCache.GetById(nodeId); - - if (node != null) - { - docreq.PublishedContent = node; - LogHelper.Debug("Cache hit, id={0}", () => nodeId); - } - else - { - docreq.RoutingContext.RoutesCache.ClearNode(nodeId); - } + docreq.PublishedContent = node; + LogHelper.Debug("Got content, id={0}", () => node.Id); + } + else + { + LogHelper.Debug("No match."); } - // if we still have no node, get it by route - if (node == null) - { - LogHelper.Debug("Cache miss, query"); - node = docreq.RoutingContext.UmbracoContext.ContentCache.GetByRoute(route); - - if (node != null) - { - docreq.PublishedContent = node; - LogHelper.Debug("Query matches, id={0}", () => docreq.PublishedContent.Id); - - var rootNodeId = docreq.Domain == null ? (int?) null : docreq.Domain.RootNodeId; - var iscanon = _doDomainLookup && !DomainHelper.ExistsDomainInPath(DomainHelper.GetAllDomains(false), node.Path, rootNodeId); - if (!iscanon) - LogHelper.Debug("Non canonical url"); - - // do not store if previewing or if non-canonical - if (!docreq.RoutingContext.UmbracoContext.InPreviewMode && iscanon) - docreq.RoutingContext.RoutesCache.Store(docreq.PublishedContent.Id, route); - - } - else - { - LogHelper.Debug("Query does not match"); - } - } - - return node; + return node; } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs b/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs index 5647426066..66ef9b7b60 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs @@ -19,7 +19,7 @@ namespace Umbraco.Web.Routing /// The PublishedContentRequest. /// A value indicating whether an Umbraco document was found and assigned. /// If successful, also assigns the template. - public override bool TryFindDocument(PublishedContentRequest docRequest) + public override bool TryFindContent(PublishedContentRequest docRequest) { IPublishedContent node = null; string path = docRequest.Uri.GetAbsolutePathDecoded(); @@ -39,7 +39,7 @@ namespace Umbraco.Web.Routing LogHelper.Debug("Valid template: \"{0}\"", () => templateAlias); var route = docRequest.HasDomain ? (docRequest.Domain.RootNodeId.ToString() + path) : path; - node = LookupDocumentNode(docRequest, route); + node = FindContent(docRequest, route); if (node != null) docRequest.TemplateModel = template; diff --git a/src/Umbraco.Web/Routing/ContentFinderByNotFoundHandler.cs b/src/Umbraco.Web/Routing/ContentFinderByNotFoundHandler.cs index 38036b5969..e4ab58fa36 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByNotFoundHandler.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByNotFoundHandler.cs @@ -15,7 +15,7 @@ namespace Umbraco.Web.Routing /// /// The PublishedContentRequest. /// A value indicating whether an Umbraco document was found and assigned. - public bool TryFindDocument(PublishedContentRequest pcr) + public bool TryFindContent(PublishedContentRequest pcr) { var type = typeof(THandler); var handler = GetHandler(type); diff --git a/src/Umbraco.Web/Routing/ContentFinderByNotFoundHandlers.cs b/src/Umbraco.Web/Routing/ContentFinderByNotFoundHandlers.cs index 7dcf8733fa..19c056a587 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByNotFoundHandlers.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByNotFoundHandlers.cs @@ -21,7 +21,7 @@ namespace Umbraco.Web.Routing /// /// The PublishedContentRequest. /// A value indicating whether an Umbraco document was found and assigned. - public bool TryFindDocument(PublishedContentRequest docRequest) + public bool TryFindContent(PublishedContentRequest docRequest) { HandlePageNotFound(docRequest); return docRequest.HasPublishedContent; @@ -55,7 +55,7 @@ namespace Umbraco.Web.Routing { var finderName = finder.GetType().FullName; LogHelper.Debug("Replace handler '{0}' by new finder '{1}'.", () => handlerName, () => finderName); - if (finder.TryFindDocument(docRequest)) + if (finder.TryFindContent(docRequest)) { // do NOT set docRequest.PublishedContent again here as // it would clear any template that the finder might have set diff --git a/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs b/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs index 8139679438..29c6650672 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs @@ -9,7 +9,7 @@ namespace Umbraco.Web.Routing /// internal class ContentFinderByPageIdQuery : IContentFinder { - public bool TryFindDocument(PublishedContentRequest docRequest) + public bool TryFindContent(PublishedContentRequest docRequest) { int pageId; if (int.TryParse(docRequest.RoutingContext.UmbracoContext.HttpContext.Request["umbPageID"], out pageId)) diff --git a/src/Umbraco.Web/Routing/ContentFinderByProfile.cs b/src/Umbraco.Web/Routing/ContentFinderByProfile.cs index 138cd0b6f9..d637f24a71 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByProfile.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByProfile.cs @@ -20,7 +20,7 @@ namespace Umbraco.Web.Routing /// /// The PublishedContentRequest. /// A value indicating whether an Umbraco document was found and assigned. - public override bool TryFindDocument(PublishedContentRequest docRequest) + public override bool TryFindContent(PublishedContentRequest docRequest) { IPublishedContent node = null; var path = docRequest.Uri.GetAbsolutePathDecoded(); @@ -38,7 +38,7 @@ namespace Umbraco.Web.Routing LogHelper.Debug("Path \"{0}\" is the profile path", () => path); var route = docRequest.HasDomain ? (docRequest.Domain.RootNodeId.ToString() + path) : path; - node = LookupDocumentNode(docRequest, route); + node = FindContent(docRequest, route); if (node != null) { diff --git a/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs b/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs index 5dc367c2c3..9c8652d1d6 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.Routing /// /// The PublishedContentRequest. /// A value indicating whether an Umbraco document was found and assigned. - public bool TryFindDocument(PublishedContentRequest docRequest) + public bool TryFindContent(PublishedContentRequest docRequest) { IPublishedContent node = null; diff --git a/src/Umbraco.Web/Routing/DefaultRoutesCache.cs b/src/Umbraco.Web/Routing/DefaultRoutesCache.cs deleted file mode 100644 index 29ecb8ac58..0000000000 --- a/src/Umbraco.Web/Routing/DefaultRoutesCache.cs +++ /dev/null @@ -1,151 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Threading; -using Umbraco.Core; -using Umbraco.Core.ObjectResolution; - -namespace Umbraco.Web.Routing -{ - /// - /// Provides a default implementation of . - /// - internal class DefaultRoutesCache : IRoutesCache - { - private ConcurrentDictionary _routes; - private ConcurrentDictionary _nodeIds; - - /// - /// Initializes a new instance of the class. - /// - public DefaultRoutesCache() : this(true) - { - - } - - internal DefaultRoutesCache(bool bindToEvents) - { - Clear(); - - if (bindToEvents) - { - Resolution.Frozen += ResolutionFrozen; - } - } - - /// - /// Once resolution is frozen, then we can bind to the events that we require - /// - /// - /// - void ResolutionFrozen(object s, EventArgs args) - { - // content - whenever the entire XML cache is rebuilt (from disk cache or from database) - // we must clear the cache entirely - global::umbraco.content.AfterRefreshContent += (sender, e) => Clear(); - - // document - whenever a document is updated in, or removed from, the XML cache - // we must clear the cache - at the moment, we clear the entire cache - // TODO could we do partial updates instead of clearing the whole cache? - global::umbraco.content.AfterUpdateDocumentCache += (sender, e) => Clear(); - global::umbraco.content.AfterClearDocumentCache += (sender, e) => Clear(); - - // domains - whenever a domain change we must clear the cache - // because routes contain the id of root nodes of domains - // TODO could we do partial updates instead of clearing the whole cache? - global::umbraco.cms.businesslogic.web.Domain.AfterDelete += (sender, e) => Clear(); - global::umbraco.cms.businesslogic.web.Domain.AfterSave += (sender, e) => Clear(); - global::umbraco.cms.businesslogic.web.Domain.New += (sender, e) => Clear(); - - // FIXME - // the content class needs to be refactored - at the moment - // content.XmlContentInternal setter does not trigger any event - // content.UpdateDocumentCache(List Documents) does not trigger any event - // content.RefreshContentFromDatabaseAsync triggers AfterRefresh _while_ refreshing - // etc... - // in addition some events do not make sense... we trigger Publish when moving - // a node, which we should not (the node is moved, not published...) etc. - } - - /// - /// Used ONLY for unit tests - /// - /// - internal IDictionary GetCachedRoutes() - { - return _routes; - } - - /// - /// Used ONLY for unit tests - /// - /// - internal IDictionary GetCachedIds() - { - return _nodeIds; - } - - /// - /// Stores a route for a node. - /// - /// The node identified. - /// The route. - public void Store(int nodeId, string route) - { - _routes.AddOrUpdate(nodeId, i => route, (i, s) => route); - _nodeIds.AddOrUpdate(route, i => nodeId, (i, s) => nodeId); - } - - /// - /// Gets a route for a node. - /// - /// The node identifier. - /// The route for the node, else null. - public string GetRoute(int nodeId) - { - string val; - _routes.TryGetValue(nodeId, out val); - return val; - } - - /// - /// Gets a node for a route. - /// - /// The route. - /// The node identified for the route, else zero. - public int GetNodeId(string route) - { - int val; - _nodeIds.TryGetValue(route, out val); - return val; - } - - /// - /// Clears the route for a node. - /// - /// The node identifier. - public void ClearNode(int nodeId) - { - if (_routes.ContainsKey(nodeId)) - { - string key; - if (_routes.TryGetValue(nodeId, out key)) - { - int val; - _nodeIds.TryRemove(key, out val); - string val2; - _routes.TryRemove(nodeId, out val2); - } - } - } - - /// - /// Clears all routes. - /// - public void Clear() - { - _routes = new ConcurrentDictionary(); - _nodeIds = new ConcurrentDictionary(); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index 6347985aeb..b6ff0f33bc 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -31,69 +31,25 @@ namespace Umbraco.Web.Routing /// public virtual string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode) { - DomainAndUri domainUri; - string path; - if (!current.IsAbsoluteUri) -// ReSharper disable LocalizableElement throw new ArgumentException("Current url must be absolute.", "current"); -// ReSharper restore LocalizableElement - // do not read cache if previewing - var route = umbracoContext.InPreviewMode - ? null - : umbracoContext.RoutingContext.RoutesCache.GetRoute(id); + // will not use cache if previewing + var route = umbracoContext.ContentCache.GetRouteById(id); - if (!string.IsNullOrEmpty(route)) + if (string.IsNullOrWhiteSpace(route)) { - // there was a route in the cache - extract domainUri and path - // route is / or / - var pos = route.IndexOf('/'); - path = pos == 0 ? route : route.Substring(pos); - domainUri = pos == 0 ? null : DomainHelper.DomainForNode(int.Parse(route.Substring(0, pos)), current); + LogHelper.Warn( + "Couldn't find any page with nodeId={0}. This is most likely caused by the page not being published.", + () => id); + return null; } - else - { - // there was no route in the cache - create a route - var node = umbracoContext.ContentCache.GetById(id); - if (node == null) - { - LogHelper.Warn( - "Couldn't find any page with nodeId={0}. This is most likely caused by the page not being published.", - () => id); - return null; - } - - // walk up from that node until we hit a node with a domain, - // or we reach the content root, collecting urls in the way - var pathParts = new List(); - var n = node; - domainUri = DomainHelper.DomainForNode(n.Id, current); - while (domainUri == null && n != null) // n is null at root - { - // get the url - var urlName = n.UrlName; - pathParts.Add(urlName); - - // move to parent node - n = n.Parent; - domainUri = n == null ? null : DomainHelper.DomainForNode(n.Id, current); - } - - // no domain, respect HideTopLevelNodeFromPath for legacy purposes - if (domainUri == null && global::umbraco.GlobalSettings.HideTopLevelNodeFromPath) - ApplyHideTopLevelNodeFromPath(umbracoContext, node, pathParts); - - // assemble the route - pathParts.Reverse(); - path = "/" + string.Join("/", pathParts); // will be "/" or "/foo" or "/foo/bar" etc - route = (n == null ? "" : n.Id.ToString()) + path; - - // do not store if previewing - if (!umbracoContext.InPreviewMode) - umbracoContext.RoutingContext.RoutesCache.Store(id, route); - } + // extract domainUri and path + // route is / or / + var pos = route.IndexOf('/'); + var path = pos == 0 ? route : route.Substring(pos); + var domainUri = pos == 0 ? null : DomainHelper.DomainForNode(int.Parse(route.Substring(0, pos)), current); // assemble the url from domainUri (maybe null) and path return AssembleUrl(domainUri, path, current, mode).ToString(); @@ -116,64 +72,22 @@ namespace Umbraco.Web.Routing /// public virtual IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current) { - string path; - IEnumerable domainUris; + // will not use cache if previewing + var route = umbracoContext.ContentCache.GetRouteById(id); - // will not read cache if previewing! - var route = umbracoContext.InPreviewMode - ? null - : umbracoContext.RoutingContext.RoutesCache.GetRoute(id); - - if (!string.IsNullOrEmpty(route)) + if (string.IsNullOrWhiteSpace(route)) { - // there was a route in the cache - extract domainUri and path - // route is / or / - int pos = route.IndexOf('/'); - path = pos == 0 ? route : route.Substring(pos); - domainUris = pos == 0 ? null : DomainHelper.DomainsForNode(int.Parse(route.Substring(0, pos)), current); + LogHelper.Warn( + "Couldn't find any page with nodeId={0}. This is most likely caused by the page not being published.", + () => id); + return null; } - else - { - // there was no route in the cache - create a route - var node = umbracoContext.ContentCache.GetById(id); - if (node == null) - { - LogHelper.Warn( - "Couldn't find any page with nodeId={0}. This is most likely caused by the page not being published.", - () => id); - return null; - } - - // walk up from that node until we hit a node with domains, - // or we reach the content root, collecting urls in the way - var pathParts = new List(); - var n = node; - domainUris = DomainHelper.DomainsForNode(n.Id, current); - while (domainUris == null && n != null) // n is null at root - { - // get the url - var urlName = node.UrlName; - pathParts.Add(urlName); - - // move to parent node - n = n.Parent; - domainUris = n == null ? null : DomainHelper.DomainsForNode(n.Id, current); - } - - // no domain, respect HideTopLevelNodeFromPath for legacy purposes - if (domainUris == null && global::umbraco.GlobalSettings.HideTopLevelNodeFromPath) - ApplyHideTopLevelNodeFromPath(umbracoContext, node, pathParts); - - // assemble the route - pathParts.Reverse(); - path = "/" + string.Join("/", pathParts); // will be "/" or "/foo" or "/foo/bar" etc - route = (n == null ? "" : n.Id.ToString()) + path; - - // do not store if previewing - if (!umbracoContext.InPreviewMode) - umbracoContext.RoutingContext.RoutesCache.Store(id, route); - } + // extract domainUri and path + // route is / or / + var pos = route.IndexOf('/'); + var path = pos == 0 ? route : route.Substring(pos); + var domainUris = pos == 0 ? null : DomainHelper.DomainsForNode(int.Parse(route.Substring(0, pos)), current); // assemble the alternate urls from domainUris (maybe empty) and path return AssembleUrls(domainUris, path).Select(uri => uri.ToString()); @@ -271,28 +185,6 @@ namespace Umbraco.Web.Routing return uris.Select(UriUtility.UriFromUmbraco); } - static void ApplyHideTopLevelNodeFromPath(UmbracoContext umbracoContext, Core.Models.IPublishedContent node, IList pathParts) - { - // in theory if hideTopLevelNodeFromPath is true, then there should be only once - // top-level node, or else domains should be assigned. but for backward compatibility - // we add this check - we look for the document matching "/" and if it's not us, then - // we do not hide the top level path - // it has to be taken care of in IPublishedContentStore.GetDocumentByRoute too so if - // "/foo" fails (looking for "/*/foo") we try also "/foo". - // this does not make much sense anyway esp. if both "/foo/" and "/bar/foo" exist, but - // that's the way it works pre-4.10 and we try to be backward compat for the time being - if (node.Parent == null) - { - var rootNode = umbracoContext.ContentCache.GetByRoute("/", true); - if (rootNode.Id == node.Id) // remove only if we're the default node - pathParts.RemoveAt(pathParts.Count - 1); - } - else - { - pathParts.RemoveAt(pathParts.Count - 1); - } - } - #endregion } } diff --git a/src/Umbraco.Web/Routing/DomainHelper.cs b/src/Umbraco.Web/Routing/DomainHelper.cs index ccb1cfc159..2aefcdb8fb 100644 --- a/src/Umbraco.Web/Routing/DomainHelper.cs +++ b/src/Umbraco.Web/Routing/DomainHelper.cs @@ -72,6 +72,16 @@ namespace Umbraco.Web.Routing return domainAndUri; } + /// + /// Gets a value indicating whether a specified node has domains. + /// + /// The node identifier. + /// True if the node has domains, else false. + internal static bool NodeHasDomains(int nodeId) + { + return nodeId > 0 && GetNodeDomains(nodeId, false).Any(); + } + /// /// Find the domains for the specified node, if any, that match a specified uri. /// diff --git a/src/Umbraco.Web/Routing/IContentFinder.cs b/src/Umbraco.Web/Routing/IContentFinder.cs index 4eef138549..c71ddd0c34 100644 --- a/src/Umbraco.Web/Routing/IContentFinder.cs +++ b/src/Umbraco.Web/Routing/IContentFinder.cs @@ -11,6 +11,6 @@ namespace Umbraco.Web.Routing /// The PublishedContentRequest. /// A value indicating whether an Umbraco document was found and assigned. /// Optionally, can also assign the template or anything else on the document request, although that is not required. - bool TryFindDocument(PublishedContentRequest contentRequest); + bool TryFindContent(PublishedContentRequest contentRequest); } } \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/IRoutesCache.cs b/src/Umbraco.Web/Routing/IRoutesCache.cs deleted file mode 100644 index 9033bc746e..0000000000 --- a/src/Umbraco.Web/Routing/IRoutesCache.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace Umbraco.Web.Routing -{ - /// - /// Represents a bi-directional cache that binds node identifiers and routes. - /// - /// - /// The cache is used both for inbound (map a route to a node) and outbound (map a node to a url). - /// A route is [rootId]/path/to/node where rootId is the id of the node holding an Umbraco domain, or -1. - /// - internal interface IRoutesCache - { - /// - /// Stores a route for a node. - /// - /// The node identified. - /// The route. - void Store(int nodeId, string route); - - /// - /// Gets a route for a node. - /// - /// The node identifier. - /// The route for the node, else null. - string GetRoute(int nodeId); - - /// - /// Gets a node for a route. - /// - /// The route. - /// The node identified for the route, else zero. - int GetNodeId(string route); - - /// - /// Clears the route for a node. - /// - /// The node identifier. - void ClearNode(int nodeId); - - /// - /// Clears all routes. - /// - void Clear(); - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs index 307bcfd28a..8451bcb6f2 100644 --- a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs +++ b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs @@ -335,7 +335,7 @@ namespace Umbraco.Web.Routing { if (_routingContext.PublishedContentFinders == null) throw new InvalidOperationException("There is no finder collection."); - _routingContext.PublishedContentFinders.Any(finder => finder.TryFindDocument(_pcr)); + _routingContext.PublishedContentFinders.Any(finder => finder.TryFindContent(_pcr)); } // indicate that the published content (if any) we have at the moment is the @@ -369,7 +369,7 @@ namespace Umbraco.Web.Routing // if it fails then give up, there isn't much more that we can do var lastChance = _routingContext.PublishedContentLastChanceFinder; - if (lastChance == null || !lastChance.TryFindDocument(_pcr)) + if (lastChance == null || !lastChance.TryFindContent(_pcr)) { LogHelper.Debug("{0}Failed to find a document, give up", () => tracePrefix); break; diff --git a/src/Umbraco.Web/Routing/RoutesCacheResolver.cs b/src/Umbraco.Web/Routing/RoutesCacheResolver.cs deleted file mode 100644 index f4b98fafb6..0000000000 --- a/src/Umbraco.Web/Routing/RoutesCacheResolver.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using Umbraco.Core.ObjectResolution; - -namespace Umbraco.Web.Routing -{ - /// - /// Resolves the implementation. - /// - internal sealed class RoutesCacheResolver : SingleObjectResolverBase - { - - /// - /// Initializes a new instance of the class with an implementation. - /// - /// The implementation. - internal RoutesCacheResolver(IRoutesCache routesCache) - : base(routesCache) - { } - - - /// - /// Can be used by developers at runtime to set their IRoutesCache at app startup - /// - /// - public void SetRoutesCache(IRoutesCache routesCache) - { - Value = routesCache; - } - - /// - /// Gets or sets the implementation. - /// - public IRoutesCache RoutesCache - { - get { return Value; } - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/RoutingContext.cs b/src/Umbraco.Web/Routing/RoutingContext.cs index ed01f1e260..d332d499eb 100644 --- a/src/Umbraco.Web/Routing/RoutingContext.cs +++ b/src/Umbraco.Web/Routing/RoutingContext.cs @@ -16,19 +16,16 @@ namespace Umbraco.Web.Routing /// The document lookups resolver. /// /// The nice urls provider. - /// The routes cache. internal RoutingContext( UmbracoContext umbracoContext, IEnumerable contentFinders, IContentFinder contentLastChanceFinder, - UrlProvider urlProvider, - IRoutesCache routesCache) + UrlProvider urlProvider) { UmbracoContext = umbracoContext; PublishedContentFinders = contentFinders; PublishedContentLastChanceFinder = contentLastChanceFinder; UrlProvider = urlProvider; - RoutesCache = routesCache; } /// @@ -50,10 +47,5 @@ namespace Umbraco.Web.Routing /// Gets the urls provider. /// internal UrlProvider UrlProvider { get; private set; } - - /// - /// Gets the - /// - internal IRoutesCache RoutesCache { get; private set; } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index ebfc2d7aed..08cd74b77b 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -330,6 +330,7 @@ + @@ -595,9 +596,6 @@ - - - diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index d869ae7fee..3323c71220 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -98,8 +98,7 @@ namespace Umbraco.Web umbracoContext, ContentFinderResolver.Current.Finders, ContentLastChanceFinderResolver.Current.Finder, - urlProvider, - RoutesCacheResolver.Current.RoutesCache); + urlProvider); //assign the routing context back umbracoContext.RoutingContext = routingContext; diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index 088e9c5b40..bfe10a4277 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -313,7 +313,7 @@ namespace Umbraco.Web SiteDomainHelperResolver.Current = new SiteDomainHelperResolver(new SiteDomainHelper()); - RoutesCacheResolver.Current = new RoutesCacheResolver(new DefaultRoutesCache(_isForTesting == false)); + PublishedContentCache.UnitTesting = _isForTesting; ThumbnailProvidersResolver.Current = new ThumbnailProvidersResolver( PluginManager.Current.ResolveThumbnailProviders()); @@ -323,7 +323,6 @@ namespace Umbraco.Web CultureDictionaryFactoryResolver.Current = new CultureDictionaryFactoryResolver( new DefaultCultureDictionaryFactory()); - } }