diff --git a/src/Umbraco.Tests/DocumentLookups/LookupByNiceUrlTests.cs b/src/Umbraco.Tests/DocumentLookups/LookupByNiceUrlTests.cs index 1858d6aadc..f00d68635b 100644 --- a/src/Umbraco.Tests/DocumentLookups/LookupByNiceUrlTests.cs +++ b/src/Umbraco.Tests/DocumentLookups/LookupByNiceUrlTests.cs @@ -1,5 +1,7 @@ using System; +using System.Linq; using System.Web; +using System.Xml; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Resolving; @@ -7,40 +9,102 @@ using Umbraco.Tests.Stubs; using Umbraco.Tests.TestHelpers; using Umbraco.Web; using Umbraco.Web.Routing; +using umbraco.BusinessLogic; +using umbraco.cms.businesslogic.cache; +using umbraco.cms.businesslogic.template; namespace Umbraco.Tests.DocumentLookups { - [TestFixture] + [TestFixture, RequiresSTA] public abstract class BaseTest { [SetUp] public virtual void Initialize() { TestHelper.SetupLog4NetForTests(); - + TestHelper.InitializeDatabase(); Resolution.Freeze(); } [TearDown] public virtual void TearDown() { - ActionsResolver.Reset(); + //reset the context on global settings + Umbraco.Core.Configuration.GlobalSettings.HttpContext = null; Resolution.IsFrozen = false; + TestHelper.ClearDatabase(); + Cache.ClearAllCache(); } protected FakeHttpContextFactory GetHttpContextFactory(string url) { var factory = new FakeHttpContextFactory(url); + + //set the state helper + StateHelper.HttpContext = factory.HttpContext; + return factory; } - protected UmbracoContext GetUmbracoContext(string url) + private UmbracoContext GetUmbracoContext(string url, Template template) { - return new UmbracoContext( + var ctx = new UmbracoContext( GetHttpContextFactory(url).HttpContext, new ApplicationContext(), - new NullRoutesCache()); + new FakeRoutesCache()); + SetupUmbracoContextForTest(ctx, template); + return ctx; + } + + protected RoutingContext GetRoutingContext(string url, Template template) + { + var umbracoContext = GetUmbracoContext(url, template); + var contentStore = new ContentStore(umbracoContext); + var niceUrls = new NiceUrlProvider(contentStore, umbracoContext); + var routingRequest = new RoutingContext( + umbracoContext, + Enumerable.Empty(), + new FakeLastChanceLookup(), + contentStore, + niceUrls); + return routingRequest; + } + + /// + /// Initlializes the UmbracoContext with specific XML + /// + /// + /// + protected void SetupUmbracoContextForTest(UmbracoContext umbracoContext, Template template) + { + umbracoContext.GetXmlDelegate = () => + { + var xDoc = new XmlDocument(); + + //create a custom xml structure to return + + xDoc.LoadXml(@" + + +]> + + + + + + + + + + + + +"); + //return the custom x doc + return xDoc; + }; } } @@ -52,14 +116,16 @@ namespace Umbraco.Tests.DocumentLookups [Test] public void Test_Default_ASPX() { - var urlAsString = "http://localhost/default.aspx"; - var ctx = GetUmbracoContext(urlAsString); - var cleanUrl = new Uri(urlAsString); - var path = ctx.RequestUrl.AbsolutePath.ToLower(); + var urlAsString = "/default.aspx"; + var template = Template.MakeNew("test", new User(0)); + var routingContext = GetRoutingContext(urlAsString, template); + var cleanUrl = routingContext.UmbracoContext.HttpContext.Request.Url; + var path = routingContext.UmbracoContext.RequestUrl.AbsolutePath.ToLower(); UmbracoModule.LegacyCleanUmbPageFromQueryString(ref cleanUrl, ref path); - var docRequest = new DocumentRequest(cleanUrl, ctx); - var lookup = new LookupByNiceUrl(); + var docRequest = new DocumentRequest(cleanUrl, routingContext); + + var lookup = new LookupByNiceUrl(); var result = lookup.TrySetDocument(docRequest); } diff --git a/src/Umbraco.Tests/Stubs/FakeLastChanceLookup.cs b/src/Umbraco.Tests/Stubs/FakeLastChanceLookup.cs new file mode 100644 index 0000000000..75b2aa777b --- /dev/null +++ b/src/Umbraco.Tests/Stubs/FakeLastChanceLookup.cs @@ -0,0 +1,12 @@ +using Umbraco.Web.Routing; + +namespace Umbraco.Tests.Stubs +{ + internal class FakeLastChanceLookup : IDocumentLastChanceLookup + { + public bool TrySetDocument(DocumentRequest docRequest) + { + return false; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Stubs/NullRoutesCache.cs b/src/Umbraco.Tests/Stubs/FakeRoutesCache.cs similarity index 84% rename from src/Umbraco.Tests/Stubs/NullRoutesCache.cs rename to src/Umbraco.Tests/Stubs/FakeRoutesCache.cs index e80fd825f0..63c74e107a 100644 --- a/src/Umbraco.Tests/Stubs/NullRoutesCache.cs +++ b/src/Umbraco.Tests/Stubs/FakeRoutesCache.cs @@ -5,7 +5,7 @@ namespace Umbraco.Tests.Stubs /// /// Used for testing, does not cache anything /// - public class NullRoutesCache : IRoutesCache + public class FakeRoutesCache : IRoutesCache { public void Store(int nodeId, string route) { diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index 97e79b1d62..48b4bf55ef 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -1,15 +1,48 @@ using System; +using System.Configuration; using System.IO; using System.Reflection; +using SqlCE4Umbraco; using log4net.Config; +using umbraco; +using umbraco.DataLayer; namespace Umbraco.Tests.TestHelpers -{ +{ /// /// Common helper properties and methods useful to testing /// public static class TestHelper { + + /// + /// Clears an initialized database + /// + public static void ClearDatabase() + { + var dataHelper = DataLayerHelper.CreateSqlHelper(GlobalSettings.DbDSN) as SqlCEHelper; + if (dataHelper == null) + throw new InvalidOperationException("The sql helper for unit tests must be of type SqlCEHelper, check the ensure the connection string used for this test is set to use SQLCE"); + dataHelper.ClearDatabase(); + } + + /// + /// Initializes a new database + /// + public static void InitializeDatabase() + { + ConfigurationManager.AppSettings.Set("umbracoDbDSN", @"datalayer=SQLCE4Umbraco.SqlCEHelper,SQLCE4Umbraco;data source=|DataDirectory|\Umbraco.sdf"); + + ClearDatabase(); + + var dataHelper = DataLayerHelper.CreateSqlHelper(GlobalSettings.DbDSN); + var installer = dataHelper.Utility.CreateInstaller(); + if (installer.CanConnect) + { + installer.Install(); + } + } + /// /// Gets the current assembly directory. /// diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index a1958bd926..62cc669f8f 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -67,7 +67,8 @@ - + + diff --git a/src/Umbraco.Tests/UmbracoModuleTests.cs b/src/Umbraco.Tests/UmbracoModuleTests.cs index 26abca9dfc..d3f8c2f3ca 100644 --- a/src/Umbraco.Tests/UmbracoModuleTests.cs +++ b/src/Umbraco.Tests/UmbracoModuleTests.cs @@ -41,7 +41,7 @@ namespace Umbraco.Tests ConfigurationManager.AppSettings.Set("umbracoReservedPaths", "~/umbraco,~/install/"); ConfigurationManager.AppSettings.Set("umbracoReservedUrls", "~/config/splashes/booting.aspx,~/install/default.aspx,~/config/splashes/noNodes.aspx,~/VSEnterpriseHelper.axd"); Cache.ClearAllCache(); - InitializeDatabase(); + TestHelper.InitializeDatabase(); //create the not found handlers config using(var sw = File.CreateText(IOHelper.MapPath(SystemFiles.NotFoundhandlersConfig, false))) @@ -67,32 +67,10 @@ namespace Umbraco.Tests ConfigurationManager.AppSettings.Set("umbracoConfigurationStatus", ""); ConfigurationManager.AppSettings.Set("umbracoReservedPaths", ""); ConfigurationManager.AppSettings.Set("umbracoReservedUrls", ""); - ClearDatabase(); + TestHelper.ClearDatabase(); Cache.ClearAllCache(); } - private void ClearDatabase() - { - var dataHelper = DataLayerHelper.CreateSqlHelper(GlobalSettings.DbDSN) as SqlCEHelper; - if (dataHelper == null) - throw new InvalidOperationException("The sql helper for unit tests must be of type SqlCEHelper, check the ensure the connection string used for this test is set to use SQLCE"); - dataHelper.ClearDatabase(); - } - - private void InitializeDatabase() - { - ConfigurationManager.AppSettings.Set("umbracoDbDSN", @"datalayer=SQLCE4Umbraco.SqlCEHelper,SQLCE4Umbraco;data source=|DataDirectory|\Umbraco.sdf"); - - ClearDatabase(); - - var dataHelper = DataLayerHelper.CreateSqlHelper(GlobalSettings.DbDSN); - var installer = dataHelper.Utility.CreateInstaller(); - if (installer.CanConnect) - { - installer.Install(); - } - } - /// /// Initlializes the UmbracoContext with specific XML /// diff --git a/src/Umbraco.Web/Mvc/RenderMvcController.cs b/src/Umbraco.Web/Mvc/RenderMvcController.cs index 2e976b2a27..5c16886106 100644 --- a/src/Umbraco.Web/Mvc/RenderMvcController.cs +++ b/src/Umbraco.Web/Mvc/RenderMvcController.cs @@ -21,7 +21,7 @@ namespace Umbraco.Web.Mvc /// protected UmbracoContext UmbracoContext { - get { return DocumentRequest.UmbracoContext; } + get { return DocumentRequest.RoutingContext.UmbracoContext; } } //TODO: make this protected once we make DocumentRequest not internal after we figure out what it should actually contain diff --git a/src/Umbraco.Web/Routing/DocumentLookupBase.cs b/src/Umbraco.Web/Routing/DocumentLookupBase.cs new file mode 100644 index 0000000000..5276d7ffb9 --- /dev/null +++ b/src/Umbraco.Web/Routing/DocumentLookupBase.cs @@ -0,0 +1,21 @@ +using System; + +namespace Umbraco.Web.Routing +{ + ///// + ///// Abstract DocumentLookup class + ///// + //internal abstract class DocumentLookupBase : IDocumentLookup + //{ + // public bool TrySetDocument(DocumentRequest docRequest) + // { + // if (docRequest == null) throw new ArgumentNullException("docRequest"); + // if (docRequest.RoutingContext == null) throw new ArgumentNullException("docRequest.RoutingContext"); + // if (docRequest.RoutingContext.UmbracoContext == null) throw new ArgumentNullException("docRequest.UmbracoContext"); + + // return TrySetDocument(docRequest, docRequest.RoutingContext, docRequest.UmbracoContext); + // } + + // protected abstract bool TrySetDocument(DocumentRequest docRequest, RoutingContext routingContext, UmbracoContext umbracoContext); + //} +} \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/DocumentRequest.cs b/src/Umbraco.Web/Routing/DocumentRequest.cs index 44f080f3b7..0c3a82f3f1 100644 --- a/src/Umbraco.Web/Routing/DocumentRequest.cs +++ b/src/Umbraco.Web/Routing/DocumentRequest.cs @@ -13,25 +13,20 @@ using umbraco.NodeFactory; using umbraco.cms.businesslogic.web; using umbraco.cms.businesslogic.template; using umbraco.cms.businesslogic.member; -using umbraco.cms.businesslogic.language; using umbraco.interfaces; namespace Umbraco.Web.Routing { - - - /// /// represents a request for one specified Umbraco document to be rendered /// by one specified template, using one particular culture. /// internal class DocumentRequest { - public DocumentRequest(Uri uri, UmbracoContext umbracoContext) + public DocumentRequest(Uri uri, RoutingContext routingContext) { this.Uri = uri; - RoutingContext = umbracoContext.RoutingContext; - UmbracoContext = umbracoContext; + RoutingContext = routingContext; } /// @@ -52,9 +47,7 @@ namespace Umbraco.Web.Routing /// Returns the current RoutingContext /// public RoutingContext RoutingContext { get; private set; } - - public UmbracoContext UmbracoContext { get; private set; } - + /// /// The cleaned up Uri used for routing /// @@ -63,9 +56,9 @@ namespace Umbraco.Web.Routing /// /// Gets or sets the document request's domain. /// - public Domain Domain { get; private set; } + public Domain Domain { get; internal set; } - public Uri DomainUri { get; private set; } + public Uri DomainUri { get; internal set; } /// /// Gets a value indicating whether the document request has a domain. @@ -158,7 +151,7 @@ namespace Umbraco.Web.Routing /// /// Gets or sets a value indicating whether the requested document could not be found. /// - public bool Is404 { get; private set; } + public bool Is404 { get; internal set; } /// /// Gets a value indicating whether the document request triggers a redirect. @@ -171,326 +164,6 @@ namespace Umbraco.Web.Routing public string RedirectUrl { get; set; } #endregion - - #region Lookup - - /// - /// Determines the site root (if any) matching the http request. - /// - /// A value indicating whether a domain was found. - internal bool LookupDomain() - { - const string tracePrefix = "LookupDomain: "; - - // note - we are not handling schemes nor ports here. - - LogHelper.Debug("{0}Uri=\"{1}\"", () => tracePrefix, () => this.Uri); - - // try to find a domain matching the current request - var domainAndUri = DomainHelper.DomainMatch(Domain.GetDomains(), UmbracoContext.UmbracoUrl, false); - - // handle domain - if (domainAndUri != null) - { - // matching an existing domain - LogHelper.Debug("{0}Matches domain=\"{1}\", rootId={2}, culture=\"{3}\"", - () => tracePrefix, - () => domainAndUri.Domain.Name, - () => domainAndUri.Domain.RootNodeId, - () => domainAndUri.Domain.Language.CultureAlias); - - this.Domain = domainAndUri.Domain; - this.DomainUri = domainAndUri.Uri; - this.Culture = new CultureInfo(domainAndUri.Domain.Language.CultureAlias); - - // canonical? not implemented at the moment - // if (...) - // { - // this.RedirectUrl = "..."; - // return true; - // } - } - else - { - // not matching any existing domain - LogHelper.Debug("{0}Matches no domain", () => tracePrefix); - - var defaultLanguage = Language.GetAllAsList().FirstOrDefault(); - this.Culture = defaultLanguage == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultLanguage.CultureAlias); - } - - LogHelper.Debug("{0}Culture=\"{1}\"", () => tracePrefix, () => this.Culture.Name); - - return this.Domain != null; - } - - /// - /// Determines the Umbraco document (if any) matching the http request. - /// - /// A value indicating whether a document and template nave been found. - internal bool LookupDocument() - { - const string tracePrefix = "LookupDocument: "; - LogHelper.Debug("{0}Path=\"{1}\"", () => tracePrefix, () => this.Uri.AbsolutePath); - - // look for the document - // the first successful resolver, if any, will set this.Node, and may also set this.Template - // some lookups may implement caching - - using (DisposableTimer.DebugDuration( - string.Format("{0}Begin resolvers", tracePrefix), - string.Format("{0}End resolvers, {1}", tracePrefix, (this.HasNode ? "a document was found" : "no document was found")))) - { - RoutingContext.DocumentLookups.Any(lookup => lookup.TrySetDocument(this)); - } - - // fixme - not handling umbracoRedirect - // should come after internal redirects - // so after ResolveDocument2() => docreq.IsRedirect => handled by the module! - - // handle not-found, redirects, access, template - LookupDocument2(); - - // handle umbracoRedirect (moved from umbraco.page) - FollowRedirect(); - - bool resolved = this.HasNode && this.HasTemplate; - return resolved; - } - - /// - /// Performs the document resolution second pass. - /// - /// The second pass consists in handling "not found", internal redirects, access validation, and template. - private void LookupDocument2() - { - const string tracePrefix = "LookupDocument2: "; - - // handle "not found", follow internal redirects, validate access, template - // because these might loop, we have to have some sort of infinite loop detection - int i = 0, j = 0; - const int maxLoop = 12; - do - { - LogHelper.Debug("{0}{1}", () => tracePrefix, () => (i == 0 ? "Begin" : "Loop")); - - // handle not found - if (!this.HasNode) - { - this.Is404 = true; - LogHelper.Debug("{0}No document, try last chance lookup", () => tracePrefix); - - // if it fails then give up, there isn't much more that we can do - var lastChance = RoutingContext.DocumentLastChanceLookup; - if (lastChance == null || !lastChance.TrySetDocument(this)) - { - LogHelper.Debug("{0}Failed to find a document, give up", () => tracePrefix); - break; - } - else - { - LogHelper.Debug("{0}Found a document", () => tracePrefix); - } - } - - // follow internal redirects as long as it's not running out of control ie infinite loop of some sort - j = 0; - while (FollowInternalRedirects() && j++ < maxLoop) ; - if (j == maxLoop) // we're running out of control - break; - - // ensure access - if (this.HasNode) - EnsureNodeAccess(); - - // resolve template - if (this.HasNode) - LookupTemplate(); - - // loop while we don't have page, ie the redirect or access - // got us to nowhere and now we need to run the notFoundLookup again - // as long as it's not running out of control ie infinite loop of some sort - - } while (!this.HasNode && i++ < maxLoop); - - if (i == maxLoop || j == maxLoop) - { - LogHelper.Debug("{0}Looks like we're running into an infinite loop, abort", () => tracePrefix); - this.XmlNode = null; - } - LogHelper.Debug("{0}End", () => tracePrefix); - } - - /// - /// Follows internal redirections through the umbracoInternalRedirectId document property. - /// - /// A value indicating whether redirection took place and led to a new published document. - /// Redirecting to a different site root and/or culture will not pick the new site root nor the new culture. - private bool FollowInternalRedirects() - { - const string tracePrefix = "FollowInternalRedirects: "; - - if (this.XmlNode == null) - throw new InvalidOperationException("There is no node."); - - bool redirect = false; - string internalRedirect = RoutingContext.ContentStore.GetNodeProperty(this.XmlNode, "umbracoInternalRedirectId"); - - if (!string.IsNullOrWhiteSpace(internalRedirect)) - { - LogHelper.Debug("{0}Found umbracoInternalRedirectId={1}", () => tracePrefix, () => internalRedirect); - - int internalRedirectId; - if (!int.TryParse(internalRedirect, out internalRedirectId)) - internalRedirectId = -1; - - if (internalRedirectId <= 0) - { - // bad redirect - this.XmlNode = null; - LogHelper.Debug("{0}Failed to redirect to id={1}: invalid value", () => tracePrefix, () => internalRedirect); - } - else if (internalRedirectId == this.NodeId) - { - // redirect to self - LogHelper.Debug("{0}Redirecting to self, ignore", () => tracePrefix); - } - else - { - // redirect to another page - var node = RoutingContext.ContentStore.GetNodeById(internalRedirectId); - this.XmlNode = node; - if (node != null) - { - redirect = true; - LogHelper.Debug("{0}Redirecting to id={1}", () => tracePrefix, () => internalRedirectId); - } - else - { - LogHelper.Debug("{0}Failed to redirect to id={1}: no such published document", () => tracePrefix, () => internalRedirectId); - } - } - } - - return redirect; - } - - /// - /// Ensures that access to current node is permitted. - /// - /// Redirecting to a different site root and/or culture will not pick the new site root nor the new culture. - private void EnsureNodeAccess() - { - const string tracePrefix = "EnsurePageAccess: "; - - if (this.XmlNode == null) - throw new InvalidOperationException("There is no node."); - - var path = RoutingContext.ContentStore.GetNodeProperty(this.XmlNode, "@path"); - - if (Access.IsProtected(this.NodeId, path)) - { - LogHelper.Debug("{0}Page is protected, check for access", () => tracePrefix); - - var user = System.Web.Security.Membership.GetUser(); - - if (user == null || !Member.IsLoggedOn()) - { - LogHelper.Debug("{0}Not logged in, redirect to login page", () => tracePrefix); - var loginPageId = Access.GetLoginPage(path); - if (loginPageId != this.NodeId) - this.XmlNode = RoutingContext.ContentStore.GetNodeById(loginPageId); - } - else if (!Access.HasAccces(this.NodeId, user.ProviderUserKey)) - { - LogHelper.Debug("{0}Current member has not access, redirect to error page", () => tracePrefix); - var errorPageId = Access.GetErrorPage(path); - if (errorPageId != this.NodeId) - this.XmlNode = RoutingContext.ContentStore.GetNodeById(errorPageId); - } - else - { - LogHelper.Debug("{0}Current member has access", () => tracePrefix); - } - } - else - { - LogHelper.Debug("{0}Page is not protected", () => tracePrefix); - } - } - - /// - /// Resolves a template for the current node. - /// - private void LookupTemplate() - { - const string tracePrefix = "LookupTemplate: "; - - if (this.XmlNode == null) - throw new InvalidOperationException("There is no node."); - - var templateAlias = UmbracoContext.HttpContext.Request.QueryString["altTemplate"]; - if (string.IsNullOrWhiteSpace(templateAlias)) - templateAlias = UmbracoContext.HttpContext.Request.Form["altTemplate"]; - - // fixme - we might want to support cookies?!? NO but provide a hook to change the template - - if (!this.HasTemplate || !string.IsNullOrWhiteSpace(templateAlias)) - { - if (string.IsNullOrWhiteSpace(templateAlias)) - { - templateAlias = RoutingContext.ContentStore.GetNodeProperty(this.XmlNode, "@template"); - LogHelper.Debug("{0}Look for template id={1}", () => tracePrefix, () => templateAlias); - int templateId; - if (!int.TryParse(templateAlias, out templateId)) - templateId = 0; - this.Template = templateId > 0 ? new Template(templateId) : null; - } - else - { - LogHelper.Debug("{0}Look for template alias=\"{1}\" (altTemplate)", () => tracePrefix, () => templateAlias); - this.Template = Template.GetByAlias(templateAlias); - } - - if (!this.HasTemplate) - { - LogHelper.Debug("{0}No template was found", () => tracePrefix); - - //TODO: I like the idea of this new setting, but lets get this in to the core at a later time, for now lets just get the basics working. - //if (Settings.HandleMissingTemplateAs404) - //{ - // this.Node = null; - // LogHelper.Debug("{0}Assume page not found (404)", tracePrefix); - //} - - // else we have no template - // and there isn't much more we can do about it - } - else - { - LogHelper.Debug("{0}Found", () => tracePrefix); - } - } - } - - /// - /// Follows external redirection through umbracoRedirect document property. - /// - private void FollowRedirect() - { - if (this.HasNode) - { - int redirectId; - if (!int.TryParse(RoutingContext.ContentStore.GetNodeProperty(this.XmlNode, "umbracoRedirect"), out redirectId)) - redirectId = -1; - string redirectUrl = "#"; - if (redirectId > 0) - redirectUrl = RoutingContext.NiceUrlProvider.GetNiceUrl(redirectId); - if (redirectUrl != "#") - this.RedirectUrl = redirectUrl; - } - } - - #endregion + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/DocumentSearcher.cs b/src/Umbraco.Web/Routing/DocumentSearcher.cs new file mode 100644 index 0000000000..0b2fd6246c --- /dev/null +++ b/src/Umbraco.Web/Routing/DocumentSearcher.cs @@ -0,0 +1,343 @@ +using System; +using System.Globalization; +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.Logging; +using umbraco.cms.businesslogic.language; +using umbraco.cms.businesslogic.member; +using umbraco.cms.businesslogic.template; +using umbraco.cms.businesslogic.web; + +namespace Umbraco.Web.Routing +{ + internal class DocumentSearcher + { + private readonly DocumentRequest _documentRequest; + private readonly UmbracoContext _umbracoContext; + private readonly RoutingContext _routingContext; + + public DocumentSearcher(DocumentRequest documentRequest) + { + _documentRequest = documentRequest; + _umbracoContext = documentRequest.RoutingContext.UmbracoContext; + _routingContext = documentRequest.RoutingContext; + } + + /// + /// Determines the site root (if any) matching the http request. + /// + /// A value indicating whether a domain was found. + internal bool LookupDomain() + { + const string tracePrefix = "LookupDomain: "; + + // note - we are not handling schemes nor ports here. + + LogHelper.Debug("{0}Uri=\"{1}\"", () => tracePrefix, () => _documentRequest.Uri); + + // try to find a domain matching the current request + var domainAndUri = DomainHelper.DomainMatch(Domain.GetDomains(), _umbracoContext.UmbracoUrl, false); + + // handle domain + if (domainAndUri != null) + { + // matching an existing domain + LogHelper.Debug("{0}Matches domain=\"{1}\", rootId={2}, culture=\"{3}\"", + () => tracePrefix, + () => domainAndUri.Domain.Name, + () => domainAndUri.Domain.RootNodeId, + () => domainAndUri.Domain.Language.CultureAlias); + + _documentRequest.Domain = domainAndUri.Domain; + _documentRequest.DomainUri = domainAndUri.Uri; + _documentRequest.Culture = new CultureInfo(domainAndUri.Domain.Language.CultureAlias); + + // canonical? not implemented at the moment + // if (...) + // { + // this.RedirectUrl = "..."; + // return true; + // } + } + else + { + // not matching any existing domain + LogHelper.Debug("{0}Matches no domain", () => tracePrefix); + + var defaultLanguage = Language.GetAllAsList().FirstOrDefault(); + _documentRequest.Culture = defaultLanguage == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultLanguage.CultureAlias); + } + + LogHelper.Debug("{0}Culture=\"{1}\"", () => tracePrefix, () => _documentRequest.Culture.Name); + + return _documentRequest.Domain != null; + } + + /// + /// Determines the Umbraco document (if any) matching the http request. + /// + /// A value indicating whether a document and template nave been found. + internal bool LookupDocument() + { + const string tracePrefix = "LookupDocument: "; + LogHelper.Debug("{0}Path=\"{1}\"", () => tracePrefix, () => _documentRequest.Uri.AbsolutePath); + + // look for the document + // the first successful resolver, if any, will set this.Node, and may also set this.Template + // some lookups may implement caching + + using (DisposableTimer.DebugDuration( + string.Format("{0}Begin resolvers", tracePrefix), + string.Format("{0}End resolvers, {1}", tracePrefix, (_documentRequest.HasNode ? "a document was found" : "no document was found")))) + { + _routingContext.DocumentLookups.Any(lookup => lookup.TrySetDocument(_documentRequest)); + } + + // fixme - not handling umbracoRedirect + // should come after internal redirects + // so after ResolveDocument2() => docreq.IsRedirect => handled by the module! + + // handle not-found, redirects, access, template + LookupDocument2(); + + // handle umbracoRedirect (moved from umbraco.page) + FollowRedirect(); + + bool resolved = _documentRequest.HasNode && _documentRequest.HasTemplate; + return resolved; + } + + /// + /// Performs the document resolution second pass. + /// + /// The second pass consists in handling "not found", internal redirects, access validation, and template. + private void LookupDocument2() + { + const string tracePrefix = "LookupDocument2: "; + + // handle "not found", follow internal redirects, validate access, template + // because these might loop, we have to have some sort of infinite loop detection + int i = 0, j = 0; + const int maxLoop = 12; + do + { + LogHelper.Debug("{0}{1}", () => tracePrefix, () => (i == 0 ? "Begin" : "Loop")); + + // handle not found + if (!_documentRequest.HasNode) + { + _documentRequest.Is404 = true; + LogHelper.Debug("{0}No document, try last chance lookup", () => tracePrefix); + + // if it fails then give up, there isn't much more that we can do + var lastChance = _routingContext.DocumentLastChanceLookup; + if (lastChance == null || !lastChance.TrySetDocument(_documentRequest)) + { + LogHelper.Debug("{0}Failed to find a document, give up", () => tracePrefix); + break; + } + else + { + LogHelper.Debug("{0}Found a document", () => tracePrefix); + } + } + + // follow internal redirects as long as it's not running out of control ie infinite loop of some sort + j = 0; + while (FollowInternalRedirects() && j++ < maxLoop) ; + if (j == maxLoop) // we're running out of control + break; + + // ensure access + if (_documentRequest.HasNode) + EnsureNodeAccess(); + + // resolve template + if (_documentRequest.HasNode) + LookupTemplate(); + + // loop while we don't have page, ie the redirect or access + // got us to nowhere and now we need to run the notFoundLookup again + // as long as it's not running out of control ie infinite loop of some sort + + } while (!_documentRequest.HasNode && i++ < maxLoop); + + if (i == maxLoop || j == maxLoop) + { + LogHelper.Debug("{0}Looks like we're running into an infinite loop, abort", () => tracePrefix); + _documentRequest.XmlNode = null; + } + LogHelper.Debug("{0}End", () => tracePrefix); + } + + /// + /// Follows internal redirections through the umbracoInternalRedirectId document property. + /// + /// A value indicating whether redirection took place and led to a new published document. + /// Redirecting to a different site root and/or culture will not pick the new site root nor the new culture. + private bool FollowInternalRedirects() + { + const string tracePrefix = "FollowInternalRedirects: "; + + if (_documentRequest.XmlNode == null) + throw new InvalidOperationException("There is no node."); + + bool redirect = false; + string internalRedirect = _routingContext.ContentStore.GetNodeProperty(_documentRequest.XmlNode, "umbracoInternalRedirectId"); + + if (!string.IsNullOrWhiteSpace(internalRedirect)) + { + LogHelper.Debug("{0}Found umbracoInternalRedirectId={1}", () => tracePrefix, () => internalRedirect); + + int internalRedirectId; + if (!int.TryParse(internalRedirect, out internalRedirectId)) + internalRedirectId = -1; + + if (internalRedirectId <= 0) + { + // bad redirect + _documentRequest.XmlNode = null; + LogHelper.Debug("{0}Failed to redirect to id={1}: invalid value", () => tracePrefix, () => internalRedirect); + } + else if (internalRedirectId == _documentRequest.NodeId) + { + // redirect to self + LogHelper.Debug("{0}Redirecting to self, ignore", () => tracePrefix); + } + else + { + // redirect to another page + var node = _routingContext.ContentStore.GetNodeById(internalRedirectId); + _documentRequest.XmlNode = node; + if (node != null) + { + redirect = true; + LogHelper.Debug("{0}Redirecting to id={1}", () => tracePrefix, () => internalRedirectId); + } + else + { + LogHelper.Debug("{0}Failed to redirect to id={1}: no such published document", () => tracePrefix, () => internalRedirectId); + } + } + } + + return redirect; + } + + /// + /// Ensures that access to current node is permitted. + /// + /// Redirecting to a different site root and/or culture will not pick the new site root nor the new culture. + private void EnsureNodeAccess() + { + const string tracePrefix = "EnsurePageAccess: "; + + if (_documentRequest.XmlNode == null) + throw new InvalidOperationException("There is no node."); + + var path = _routingContext.ContentStore.GetNodeProperty(_documentRequest.XmlNode, "@path"); + + if (Access.IsProtected(_documentRequest.NodeId, path)) + { + LogHelper.Debug("{0}Page is protected, check for access", () => tracePrefix); + + var user = System.Web.Security.Membership.GetUser(); + + if (user == null || !Member.IsLoggedOn()) + { + LogHelper.Debug("{0}Not logged in, redirect to login page", () => tracePrefix); + var loginPageId = Access.GetLoginPage(path); + if (loginPageId != _documentRequest.NodeId) + _documentRequest.XmlNode = _routingContext.ContentStore.GetNodeById(loginPageId); + } + else if (!Access.HasAccces(_documentRequest.NodeId, user.ProviderUserKey)) + { + LogHelper.Debug("{0}Current member has not access, redirect to error page", () => tracePrefix); + var errorPageId = Access.GetErrorPage(path); + if (errorPageId != _documentRequest.NodeId) + _documentRequest.XmlNode = _routingContext.ContentStore.GetNodeById(errorPageId); + } + else + { + LogHelper.Debug("{0}Current member has access", () => tracePrefix); + } + } + else + { + LogHelper.Debug("{0}Page is not protected", () => tracePrefix); + } + } + + /// + /// Resolves a template for the current node. + /// + private void LookupTemplate() + { + const string tracePrefix = "LookupTemplate: "; + + if (_documentRequest.XmlNode == null) + throw new InvalidOperationException("There is no node."); + + var templateAlias = _umbracoContext.HttpContext.Request.QueryString["altTemplate"]; + if (string.IsNullOrWhiteSpace(templateAlias)) + templateAlias = _umbracoContext.HttpContext.Request.Form["altTemplate"]; + + // fixme - we might want to support cookies?!? NO but provide a hook to change the template + + if (!_documentRequest.HasTemplate || !string.IsNullOrWhiteSpace(templateAlias)) + { + if (string.IsNullOrWhiteSpace(templateAlias)) + { + templateAlias = _routingContext.ContentStore.GetNodeProperty(_documentRequest.XmlNode, "@template"); + LogHelper.Debug("{0}Look for template id={1}", () => tracePrefix, () => templateAlias); + int templateId; + if (!int.TryParse(templateAlias, out templateId)) + templateId = 0; + _documentRequest.Template = templateId > 0 ? new Template(templateId) : null; + } + else + { + LogHelper.Debug("{0}Look for template alias=\"{1}\" (altTemplate)", () => tracePrefix, () => templateAlias); + _documentRequest.Template = Template.GetByAlias(templateAlias); + } + + if (!_documentRequest.HasTemplate) + { + LogHelper.Debug("{0}No template was found", () => tracePrefix); + + //TODO: I like the idea of this new setting, but lets get this in to the core at a later time, for now lets just get the basics working. + //if (Settings.HandleMissingTemplateAs404) + //{ + // this.Node = null; + // LogHelper.Debug("{0}Assume page not found (404)", tracePrefix); + //} + + // else we have no template + // and there isn't much more we can do about it + } + else + { + LogHelper.Debug("{0}Found", () => tracePrefix); + } + } + } + + /// + /// Follows external redirection through umbracoRedirect document property. + /// + private void FollowRedirect() + { + if (_documentRequest.HasNode) + { + int redirectId; + if (!int.TryParse(_routingContext.ContentStore.GetNodeProperty(_documentRequest.XmlNode, "umbracoRedirect"), out redirectId)) + redirectId = -1; + string redirectUrl = "#"; + if (redirectId > 0) + redirectUrl = _routingContext.NiceUrlProvider.GetNiceUrl(redirectId); + if (redirectUrl != "#") + _documentRequest.RedirectUrl = redirectUrl; + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/LookupByAlias.cs b/src/Umbraco.Web/Routing/LookupByAlias.cs index cf3abcd685..829c9d1d4f 100644 --- a/src/Umbraco.Web/Routing/LookupByAlias.cs +++ b/src/Umbraco.Web/Routing/LookupByAlias.cs @@ -13,32 +13,31 @@ namespace Umbraco.Web.Routing /// The alias is the full path to the document. There can be more than one alias, separated by commas. /// //[ResolutionWeight(50)] - internal class LookupByAlias : IDocumentLookup + internal class LookupByAlias : IDocumentLookup { - /// /// Tries to find and assign an Umbraco document to a DocumentRequest. /// - /// The DocumentRequest. + /// The DocumentRequest. /// A value indicating whether an Umbraco document was found and assigned. - public bool TrySetDocument(DocumentRequest docreq) - { - XmlNode node = null; + public bool TrySetDocument(DocumentRequest docRequest) + { + XmlNode node = null; - if (docreq.Uri.AbsolutePath != "/") // no alias if "/" - { - node = docreq.RoutingContext.ContentStore.GetNodeByUrlAlias(docreq.HasDomain ? docreq.Domain.RootNodeId : 0, docreq.Uri.AbsolutePath); - if (node != null) - { - LogHelper.Debug("Path \"{0}\" is an alias for id={1}", () => docreq.Uri.AbsolutePath, () => docreq.NodeId); - docreq.XmlNode = node; - } - } + if (docRequest.Uri.AbsolutePath != "/") // no alias if "/" + { + node = docRequest.RoutingContext.ContentStore.GetNodeByUrlAlias(docRequest.HasDomain ? docRequest.Domain.RootNodeId : 0, docRequest.Uri.AbsolutePath); + if (node != null) + { + LogHelper.Debug("Path \"{0}\" is an alias for id={1}", () => docRequest.Uri.AbsolutePath, () => docRequest.NodeId); + docRequest.XmlNode = node; + } + } - if (node == null) + if (node == null) LogHelper.Debug("Not an alias"); - return node != null; - } + return node != null; + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/LookupById.cs b/src/Umbraco.Web/Routing/LookupById.cs index 2c38cdb42a..88c3d4c014 100644 --- a/src/Umbraco.Web/Routing/LookupById.cs +++ b/src/Umbraco.Web/Routing/LookupById.cs @@ -13,21 +13,21 @@ namespace Umbraco.Web.Routing /// Handles /1234 where 1234 is the identified of a document. /// //[ResolutionWeight(20)] - internal class LookupById : IDocumentLookup + internal class LookupById : IDocumentLookup { /// /// Tries to find and assign an Umbraco document to a DocumentRequest. /// - /// The DocumentRequest. + /// The DocumentRequest. /// A value indicating whether an Umbraco document was found and assigned. - public bool TrySetDocument(DocumentRequest docreq) + public bool TrySetDocument(DocumentRequest docRequest) { XmlNode node = null; int nodeId = -1; - if (docreq.Uri.AbsolutePath != "/") // no id if "/" + if (docRequest.Uri.AbsolutePath != "/") // no id if "/" { - string noSlashPath = docreq.Uri.AbsolutePath.Substring(1); + string noSlashPath = docRequest.Uri.AbsolutePath.Substring(1); if (!Int32.TryParse(noSlashPath, out nodeId)) nodeId = -1; @@ -35,11 +35,11 @@ namespace Umbraco.Web.Routing if (nodeId > 0) { LogHelper.Debug("Id={0}", () => nodeId); - node = docreq.RoutingContext.ContentStore.GetNodeById(nodeId); + node = docRequest.RoutingContext.ContentStore.GetNodeById(nodeId); if (node != null) { - docreq.XmlNode = node; - LogHelper.Debug("Found node with id={0}", () => docreq.NodeId); + docRequest.XmlNode = node; + LogHelper.Debug("Found node with id={0}", () => docRequest.NodeId); } else { diff --git a/src/Umbraco.Web/Routing/LookupByNiceUrl.cs b/src/Umbraco.Web/Routing/LookupByNiceUrl.cs index 9cdfd5670f..ec83e4b9f1 100644 --- a/src/Umbraco.Web/Routing/LookupByNiceUrl.cs +++ b/src/Umbraco.Web/Routing/LookupByNiceUrl.cs @@ -12,21 +12,20 @@ namespace Umbraco.Web.Routing /// Handles /foo/bar where /foo/bar is the nice url of a document. /// //[ResolutionWeight(10)] - internal class LookupByNiceUrl : IDocumentLookup + internal class LookupByNiceUrl : IDocumentLookup { - /// /// Tries to find and assign an Umbraco document to a DocumentRequest. /// - /// The DocumentRequest. + /// The DocumentRequest. /// A value indicating whether an Umbraco document was found and assigned. - public virtual bool TrySetDocument(DocumentRequest docreq) + public virtual bool TrySetDocument(DocumentRequest docRequest) { string route; - if (docreq.HasDomain) - route = docreq.Domain.RootNodeId.ToString() + DomainHelper.PathRelativeToDomain(docreq.DomainUri, docreq.Uri.AbsolutePath); + if (docRequest.HasDomain) + route = docRequest.Domain.RootNodeId.ToString() + DomainHelper.PathRelativeToDomain(docRequest.DomainUri, docRequest.Uri.AbsolutePath); else - route = docreq.Uri.AbsolutePath; + route = docRequest.Uri.AbsolutePath; //TODO: When this is not IIS 7, this does not work for the root '/' request since it comes through as default.aspx!! // this needs fixing. @@ -34,7 +33,7 @@ namespace Umbraco.Web.Routing //format the path, thsi needs fixing when pre-IIS7 route = route.Replace(".aspx", ""); - var node = LookupDocumentNode(docreq, route); + var node = LookupDocumentNode(docRequest, route); return node != null; } @@ -49,8 +48,8 @@ namespace Umbraco.Web.Routing LogHelper.Debug("Test route \"{0}\"", () => route); //return '0' if in preview mode! - var nodeId = !docreq.UmbracoContext.InPreviewMode - ? docreq.UmbracoContext.RoutesCache.GetNodeId(route) + var nodeId = !docreq.RoutingContext.UmbracoContext.InPreviewMode + ? docreq.RoutingContext.UmbracoContext.RoutesCache.GetNodeId(route) : 0; @@ -65,7 +64,7 @@ namespace Umbraco.Web.Routing } else { - docreq.UmbracoContext.RoutesCache.ClearNode(nodeId); + docreq.RoutingContext.UmbracoContext.RoutesCache.ClearNode(nodeId); } } @@ -78,9 +77,9 @@ namespace Umbraco.Web.Routing docreq.XmlNode = node; LogHelper.Debug("Query matches, id={0}", () => docreq.NodeId); - if (!docreq.UmbracoContext.InPreviewMode) + if (!docreq.RoutingContext.UmbracoContext.InPreviewMode) { - docreq.UmbracoContext.RoutesCache.Store(docreq.NodeId, route); // will not write if previewing + docreq.RoutingContext.UmbracoContext.RoutesCache.Store(docreq.NodeId, route); // will not write if previewing } } diff --git a/src/Umbraco.Web/Routing/LookupByNiceUrlAndTemplate.cs b/src/Umbraco.Web/Routing/LookupByNiceUrlAndTemplate.cs index 2e83212b71..100f03fc4d 100644 --- a/src/Umbraco.Web/Routing/LookupByNiceUrlAndTemplate.cs +++ b/src/Umbraco.Web/Routing/LookupByNiceUrlAndTemplate.cs @@ -19,20 +19,20 @@ namespace Umbraco.Web.Routing /// /// Tries to find and assign an Umbraco document to a DocumentRequest. /// - /// The DocumentRequest. + /// The DocumentRequest. /// A value indicating whether an Umbraco document was found and assigned. /// If successful, also assigns the template. - public override bool TrySetDocument(DocumentRequest docreq) + public override bool TrySetDocument(DocumentRequest docRequest) { XmlNode node = null; - string path = docreq.Uri.AbsolutePath; + string path = docRequest.Uri.AbsolutePath; - if (docreq.HasDomain) - path = DomainHelper.PathRelativeToDomain(docreq.DomainUri, path); + if (docRequest.HasDomain) + path = DomainHelper.PathRelativeToDomain(docRequest.DomainUri, path); if (path != "/") // no template if "/" { - var pos = docreq.Uri.AbsolutePath.LastIndexOf('/'); - var templateAlias = docreq.Uri.AbsolutePath.Substring(pos + 1); + var pos = docRequest.Uri.AbsolutePath.LastIndexOf('/'); + var templateAlias = docRequest.Uri.AbsolutePath.Substring(pos + 1); path = path.Substring(0, pos); var template = Template.GetByAlias(templateAlias); @@ -40,11 +40,11 @@ namespace Umbraco.Web.Routing { LogHelper.Debug("Valid template: \"{0}\"", () => templateAlias); - var route = docreq.HasDomain ? (docreq.Domain.RootNodeId.ToString() + path) : path; - node = LookupDocumentNode(docreq, route); + var route = docRequest.HasDomain ? (docRequest.Domain.RootNodeId.ToString() + path) : path; + node = LookupDocumentNode(docRequest, route); if (node != null) - docreq.Template = template; + docRequest.Template = template; } else { diff --git a/src/Umbraco.Web/Routing/LookupByProfile.cs b/src/Umbraco.Web/Routing/LookupByProfile.cs index 5e5b225313..cd065e7ff8 100644 --- a/src/Umbraco.Web/Routing/LookupByProfile.cs +++ b/src/Umbraco.Web/Routing/LookupByProfile.cs @@ -15,37 +15,36 @@ namespace Umbraco.Web.Routing /// We keep it for backward compatility reasons. /// //[ResolutionWeight(40)] - internal class LookupByProfile : LookupByNiceUrl, IDocumentLookup + internal class LookupByProfile : LookupByNiceUrl { - /// /// Tries to find and assign an Umbraco document to a DocumentRequest. /// - /// The DocumentRequest. + /// The DocumentRequest. /// A value indicating whether an Umbraco document was found and assigned. - public override bool TrySetDocument(DocumentRequest docreq) + public override bool TrySetDocument(DocumentRequest docRequest) { XmlNode node = null; bool isProfile = false; - var pos = docreq.Uri.AbsolutePath.LastIndexOf('/'); + var pos = docRequest.Uri.AbsolutePath.LastIndexOf('/'); if (pos > 0) { - var memberLogin = docreq.Uri.AbsolutePath.Substring(pos + 1); - var path = docreq.Uri.AbsolutePath.Substring(0, pos); + var memberLogin = docRequest.Uri.AbsolutePath.Substring(pos + 1); + var path = docRequest.Uri.AbsolutePath.Substring(0, pos); if (path == GlobalSettings.ProfileUrl) { isProfile = true; - LogHelper.Debug("Path \"{0}\" is the profile path", () => path); + LogHelper.Debug("Path \"{0}\" is the profile path", () => path); - var route = docreq.HasDomain ? (docreq.Domain.RootNodeId.ToString() + path) : path; - node = LookupDocumentNode(docreq, route); + var route = docRequest.HasDomain ? (docRequest.Domain.RootNodeId.ToString() + path) : path; + node = LookupDocumentNode(docRequest, route); if (node != null) { //TODO: Should be handled by Context Items class manager (http://issues.umbraco.org/issue/U4-61) - docreq.UmbracoContext.HttpContext.Items["umbMemberLogin"] = memberLogin; + docRequest.RoutingContext.UmbracoContext.HttpContext.Items["umbMemberLogin"] = memberLogin; } else { diff --git a/src/Umbraco.Web/Routing/RoutingContext.cs b/src/Umbraco.Web/Routing/RoutingContext.cs index 74b2bb51d1..d88aa4359e 100644 --- a/src/Umbraco.Web/Routing/RoutingContext.cs +++ b/src/Umbraco.Web/Routing/RoutingContext.cs @@ -12,29 +12,28 @@ namespace Umbraco.Web.Routing /// /// Initializes a new instance of the class. /// - /// The Umbraco context. /// The document lookups resolver. /// /// The content store. /// The nice urls resolver. internal RoutingContext( - //UmbracoContext umbracoContext, + UmbracoContext umbracoContext, IEnumerable documentLookups, IDocumentLastChanceLookup documentLastChanceLookup, ContentStore contentStore, NiceUrlProvider niceUrlResolver) { - //this.UmbracoContext = umbracoContext; + this.UmbracoContext = umbracoContext; this.DocumentLookups = documentLookups; DocumentLastChanceLookup = documentLastChanceLookup; this.ContentStore = contentStore; this.NiceUrlProvider = niceUrlResolver; } - ///// - ///// Gets the Umbraco context. - ///// - //public UmbracoContext UmbracoContext { get; private set; } + /// + /// Gets the Umbraco context. + /// + public UmbracoContext UmbracoContext { get; private set; } /// /// Gets the document lookups resolver. diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 5a425a07d1..7fe3391154 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -254,6 +254,8 @@ + + diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index 95423584d1..7e9d9515d4 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -193,20 +193,20 @@ namespace Umbraco.Web /// /// Boolean value indicating whether the current request is a front-end umbraco request /// - public bool IsFrontEndUmbracoRequest - { - get { return DocumentRequest != null; } - } + public bool IsFrontEndUmbracoRequest + { + get { return DocumentRequest != null; } + } /// /// Gets/sets the RoutingContext object /// internal RoutingContext RoutingContext { get; set; } - /// - /// Gets/sets the DocumentRequest object - /// - internal DocumentRequest DocumentRequest { get; set; } + /// + /// Gets/sets the DocumentRequest object + /// + internal DocumentRequest DocumentRequest { get; set; } /// /// Exposes the HttpContext for the current request diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index 27fdb157f4..d0c162c720 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -64,6 +64,7 @@ namespace Umbraco.Web var niceUrls = new NiceUrlProvider(contentStore, umbracoContext); //create the RoutingContext var routingContext = new RoutingContext( + umbracoContext, DocumentLookupsResolver.Current.DocumentLookups, LastChanceLookupResolver.Current.LastChanceLookup, contentStore, @@ -83,8 +84,8 @@ namespace Umbraco.Web //Create a document request since we are rendering a document on the front-end // create the new document request which will cleanup the uri once and for all - var docreq = new DocumentRequest(uri, umbracoContext); - //assign the routing context to the umbraco context + var docreq = new DocumentRequest(uri, routingContext); + //assign the document request to the umbraco context now that we know its a front end request umbracoContext.DocumentRequest = docreq; // note - at that point the original legacy module did something do handle IIS custom 404 errors @@ -95,14 +96,21 @@ namespace Umbraco.Web // to trigger Umbraco's not-found, one should configure IIS and/or ASP.NET custom 404 errors // so that they point to a non-existing page eg /redirect-404.aspx - docreq.LookupDomain(); + //create the searcher + var searcher = new DocumentSearcher(docreq); + //find domain + searcher.LookupDomain(); + //redirect if it has been flagged if (docreq.IsRedirect) httpContext.Response.Redirect(docreq.RedirectUrl, true); + //set the culture on the thread Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = docreq.Culture; - docreq.LookupDocument(); + //find the document + searcher.LookupDocument(); + //redirect if it has been flagged if (docreq.IsRedirect) httpContext.Response.Redirect(docreq.RedirectUrl, true); - + //if no doc is found, send to our not found handler if (docreq.Is404) { httpContext.RemapHandler(new DocumentNotFoundHttpHandler());