diff --git a/src/Umbraco.Web/ContentStore.cs b/src/Umbraco.Web/ContentStore.cs index d123342f22..e13714c9ac 100644 --- a/src/Umbraco.Web/ContentStore.cs +++ b/src/Umbraco.Web/ContentStore.cs @@ -12,8 +12,15 @@ namespace Umbraco.Web /// internal class ContentStore { - private readonly UmbracoContext _umbracoContext; + /// + /// Delegate to return the current UmbracoContext + /// + private readonly UmbracoContext _umbracoContext; + /// + /// Constructor accepting a delegate to resolve the UmbracoContext + /// + /// public ContentStore(UmbracoContext umbracoContext) { _umbracoContext = umbracoContext; diff --git a/src/Umbraco.Web/NiceUrls.cs b/src/Umbraco.Web/NiceUrlResolver.cs similarity index 86% rename from src/Umbraco.Web/NiceUrls.cs rename to src/Umbraco.Web/NiceUrlResolver.cs index ae2ff6cf70..1e7da3b26e 100644 --- a/src/Umbraco.Web/NiceUrls.cs +++ b/src/Umbraco.Web/NiceUrlResolver.cs @@ -7,9 +7,12 @@ using umbraco.cms.businesslogic.web; namespace Umbraco.Web { - internal class NiceUrls + /// + /// Resolves NiceUrls for a given node id + /// + internal class NiceUrlResolver { - public NiceUrls(ContentStore contentStore, UmbracoContext umbracoContext, RoutesCache routesCache) + public NiceUrlResolver(ContentStore contentStore, UmbracoContext umbracoContext, IRoutesCache routesCache) { _umbracoContext = umbracoContext; _contentStore = contentStore; @@ -18,7 +21,7 @@ namespace Umbraco.Web private readonly UmbracoContext _umbracoContext; private readonly ContentStore _contentStore; - private readonly RoutesCache _routesCache; + private readonly IRoutesCache _routesCache; // note: this could be a parameter... const string UrlNameProperty = "@urlName"; @@ -34,10 +37,13 @@ namespace Umbraco.Web public virtual string GetNiceUrl(int nodeId, int startNodeDepth, bool forceDomain) { - string route; - string path; + string path; + + // will not read cache if previewing! + var route = !_umbracoContext.InPreviewMode + ? _routesCache.GetRoute(nodeId) + : null; - route = _routesCache.GetRoute(nodeId); // will not read cache if previewing if (route != null) { int pos = route.IndexOf('/'); @@ -85,7 +91,11 @@ namespace Umbraco.Web parts.Reverse(); path = "/" + string.Join("/", parts); route = string.Format("{0}{1}", id, path); - _routesCache.Store(nodeId, route); // will not write if previewing + + if (!_umbracoContext.InPreviewMode) + { + _routesCache.Store(nodeId, route); // will not write if previewing + } return FormatUrl(domain, path); } diff --git a/src/Umbraco.Web/Routing/DefaultRoutesCache.cs b/src/Umbraco.Web/Routing/DefaultRoutesCache.cs new file mode 100644 index 0000000000..a755e9c489 --- /dev/null +++ b/src/Umbraco.Web/Routing/DefaultRoutesCache.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Web.Routing +{ + /// + /// The default implementation of IRoutesCache + /// + internal class DefaultRoutesCache : IRoutesCache + { + private readonly object _lock = new object(); + private Dictionary _routes; + private Dictionary _nodeIds; + + public DefaultRoutesCache() + { + Clear(); + + // here we should register handlers to clear the cache when content changes + // at the moment this is done by library, which clears everything when content changed + // + // but really, we should do some partial refreshes! + // otherwise, we could even cache 404 errors... + } + + public void Store(int nodeId, string route) + { + lock (_lock) + { + _routes[nodeId] = route; + _nodeIds[route] = nodeId; + } + } + + public string GetRoute(int nodeId) + { + lock (_lock) + { + return _routes.ContainsKey(nodeId) ? _routes[nodeId] : null; + } + } + + public int GetNodeId(string route) + { + lock (_lock) + { + return _nodeIds.ContainsKey(route) ? _nodeIds[route] : 0; + } + } + + public void ClearNode(int nodeId) + { + lock (_lock) + { + if (_routes.ContainsKey(nodeId)) + { + _nodeIds.Remove(_routes[nodeId]); + _routes.Remove(nodeId); + } + } + } + + public void Clear() + { + lock (_lock) + { + _routes = new Dictionary(); + _nodeIds = new Dictionary(); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/DocumentRequest.cs b/src/Umbraco.Web/Routing/DocumentRequest.cs index efa44a1881..65844d84c8 100644 --- a/src/Umbraco.Web/Routing/DocumentRequest.cs +++ b/src/Umbraco.Web/Routing/DocumentRequest.cs @@ -16,12 +16,12 @@ namespace Umbraco.Web.Routing { static readonly TraceSource Trace = new TraceSource("DocumentRequest"); - public DocumentRequest(Uri uri, RoutingEnvironment lookups, UmbracoContext umbracoContext, NiceUrls niceUrls) + public DocumentRequest(Uri uri, RoutingEnvironment lookups, UmbracoContext umbracoContext, NiceUrlResolver niceUrlResolver) { // register lookups _environment = lookups; - _umbracoContext = umbracoContext; - _niceUrls = niceUrls; + UmbracoContext = umbracoContext; + _niceUrlResolver = niceUrlResolver; // prepare the host var host = uri.Host; @@ -55,13 +55,25 @@ namespace Umbraco.Web.Routing this.QueryString = uri.Query.TrimStart('?'); } + readonly RoutingEnvironment _environment; + private readonly NiceUrlResolver _niceUrlResolver; + + /// + /// the id of the requested node, if any, else zero. + /// + int _nodeId = 0; + + /// + /// the requested node, if any, else null. + /// + XmlNode _node = null; + #region Properties - // the id of the requested node, if any, else zero. - int _nodeId = 0; - - // the requested node, if any, else null. - XmlNode _node = null; + /// + /// Returns the current UmbracoContext + /// + public UmbracoContext UmbracoContext { get; private set; } /// /// Gets the request host name. @@ -176,15 +188,9 @@ namespace Umbraco.Web.Routing #region Resolve - readonly RoutingEnvironment _environment; - private readonly UmbracoContext _umbracoContext; - private readonly NiceUrls _niceUrls; - /// /// Determines the site root (if any) matching the http request. - /// - /// The host name part of the http request, eg. www.example.com. - /// The url part of the http request, starting with a slash, eg. /foo/bar. + /// /// A value indicating whether a domain was found. public bool ResolveSiteRoot() { @@ -432,9 +438,9 @@ namespace Umbraco.Web.Routing if (this.Node == null) throw new InvalidOperationException("There is no node."); - var templateAlias = _umbracoContext.HttpContext.Request.QueryString["altTemplate"]; + var templateAlias = UmbracoContext.HttpContext.Request.QueryString["altTemplate"]; if (string.IsNullOrWhiteSpace(templateAlias)) - templateAlias = _umbracoContext.HttpContext.Request.Form["altTemplate"]; + templateAlias = UmbracoContext.HttpContext.Request.Form["altTemplate"]; // fixme - we might want to support cookies?!? NO but provide a hook to change the template @@ -488,7 +494,7 @@ namespace Umbraco.Web.Routing redirectId = -1; string redirectUrl = "#"; if (redirectId > 0) - redirectUrl = _niceUrls.GetNiceUrl(redirectId); + redirectUrl = _niceUrlResolver.GetNiceUrl(redirectId); if (redirectUrl != "#") this.RedirectUrl = redirectUrl; } diff --git a/src/Umbraco.Web/Routing/IRoutesCache.cs b/src/Umbraco.Web/Routing/IRoutesCache.cs new file mode 100644 index 0000000000..0f4a1044ec --- /dev/null +++ b/src/Umbraco.Web/Routing/IRoutesCache.cs @@ -0,0 +1,11 @@ +namespace Umbraco.Web.Routing +{ + internal interface IRoutesCache + { + void Store(int nodeId, string route); + string GetRoute(int nodeId); + int GetNodeId(string route); + void ClearNode(int nodeId); + void Clear(); + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/LookupByPath.cs b/src/Umbraco.Web/Routing/LookupByPath.cs index d5e9dbba19..30dc31f546 100644 --- a/src/Umbraco.Web/Routing/LookupByPath.cs +++ b/src/Umbraco.Web/Routing/LookupByPath.cs @@ -9,7 +9,7 @@ namespace Umbraco.Web.Routing [LookupWeight(10)] internal class LookupByPath : ILookup { - public LookupByPath(ContentStore contentStore, RoutesCache routesCache) + public LookupByPath(ContentStore contentStore, IRoutesCache routesCache) { ContentStore = contentStore; RoutesCache = routesCache; @@ -18,7 +18,7 @@ namespace Umbraco.Web.Routing static readonly TraceSource Trace = new TraceSource("LookupByPath"); protected ContentStore ContentStore; - protected RoutesCache RoutesCache; + protected IRoutesCache RoutesCache; public virtual bool LookupDocument(DocumentRequest docreq) { @@ -31,7 +31,12 @@ namespace Umbraco.Web.Routing { Trace.TraceInformation("Test route \"{0}\"", route); - var nodeId = RoutesCache.GetNodeId(route); + //return '0' if in preview mode! + var nodeId = !docreq.UmbracoContext.InPreviewMode + ? RoutesCache.GetNodeId(route) + : 0; + + XmlNode node = null; if (nodeId > 0) { @@ -55,7 +60,12 @@ namespace Umbraco.Web.Routing { docreq.Node = node; Trace.TraceInformation("Query matches, id={0}", docreq.NodeId); - RoutesCache.Store(docreq.NodeId, route); + + if (!docreq.UmbracoContext.InPreviewMode) + { + RoutesCache.Store(docreq.NodeId, route); // will not write if previewing + } + } else { diff --git a/src/Umbraco.Web/Routing/LookupByPathWithTemplate.cs b/src/Umbraco.Web/Routing/LookupByPathWithTemplate.cs index 1b91be23aa..11454bcdb2 100644 --- a/src/Umbraco.Web/Routing/LookupByPathWithTemplate.cs +++ b/src/Umbraco.Web/Routing/LookupByPathWithTemplate.cs @@ -13,7 +13,7 @@ namespace Umbraco.Web.Routing { static readonly TraceSource Trace = new TraceSource("LookupByPathWithTemplate"); - public LookupByPathWithTemplate(ContentStore contentStore, RoutesCache routesCache) + public LookupByPathWithTemplate(ContentStore contentStore, IRoutesCache routesCache) : base(contentStore, routesCache) { } diff --git a/src/Umbraco.Web/Routing/LookupByProfile.cs b/src/Umbraco.Web/Routing/LookupByProfile.cs index a65eafac2e..c3044349e7 100644 --- a/src/Umbraco.Web/Routing/LookupByProfile.cs +++ b/src/Umbraco.Web/Routing/LookupByProfile.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.Routing static readonly TraceSource Trace = new TraceSource("LookupByProfile"); - public LookupByProfile(ContentStore contentStore, RoutesCache routesCache, UmbracoContext umbracoContext) + public LookupByProfile(ContentStore contentStore, IRoutesCache routesCache, UmbracoContext umbracoContext) : base(contentStore, routesCache) { _umbracoContext = umbracoContext; diff --git a/src/Umbraco.Web/Routing/RoutesCache.cs b/src/Umbraco.Web/Routing/RoutesCache.cs index d366dc23f9..dcb7843e1b 100644 --- a/src/Umbraco.Web/Routing/RoutesCache.cs +++ b/src/Umbraco.Web/Routing/RoutesCache.cs @@ -1,81 +1,57 @@ -using System.Collections.Generic; +using System; namespace Umbraco.Web.Routing { - internal class RoutesCache - { - private readonly object _lock = new object(); - private Dictionary _routes; - private Dictionary _nodeIds; + /// + /// A singly registered object to assign and get the current IRoutesCache provider + /// + internal class RoutesCache + { + private static readonly RoutesCache Instance = new RoutesCache(); + private static IRoutesCache _provider; - private readonly UmbracoContext _umbracoContext; + /// + /// public contructor assigns the DefaultRoutesCache as the default provider + /// + public RoutesCache() + :this(null) + { + } - public RoutesCache(UmbracoContext umbracoContext) - { - _umbracoContext = umbracoContext; + /// + /// Constructor sets a custom provider if specified + /// + /// + internal RoutesCache(IRoutesCache provider) + { + _provider = provider ?? new DefaultRoutesCache(); + } - Clear(); + /// + /// Set the routes cache provider + /// + /// + public static void SetProvider(IRoutesCache provider) + { + if (provider == null) throw new ArgumentNullException("provider"); + _provider = provider; + } - // here we should register handlers to clear the cache when content changes - // at the moment this is done by library, which clears everything when content changed - // - // but really, we should do some partial refreshes! - // otherwise, we could even cache 404 errors... - } + /// + /// Singleton accessor + /// + public static RoutesCache Current + { + get { return Instance; } + } - public void Store(int nodeId, string route) - { - if (_umbracoContext.InPreviewMode) - return; - - lock (_lock) - { - _routes[nodeId] = route; - _nodeIds[route] = nodeId; - } - } - - public string GetRoute(int nodeId) - { - if (_umbracoContext.InPreviewMode) - return null; - - lock (_lock) - { - return _routes.ContainsKey(nodeId) ? _routes[nodeId] : null; - } - } - - public int GetNodeId(string route) - { - if (_umbracoContext.InPreviewMode) - return 0; - - lock (_lock) - { - return _nodeIds.ContainsKey(route) ? _nodeIds[route] : 0; - } - } - - public void ClearNode(int nodeId) - { - lock (_lock) - { - if (_routes.ContainsKey(nodeId)) - { - _nodeIds.Remove(_routes[nodeId]); - _routes.Remove(nodeId); - } - } - } - - public void Clear() - { - lock (_lock) - { - _routes = new Dictionary(); - _nodeIds = new Dictionary(); - } - } - } + /// + /// Get the current provider + /// + /// + public IRoutesCache GetProvider() + { + return _provider; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/RoutingEnvironment.cs b/src/Umbraco.Web/Routing/RoutingEnvironment.cs index cf848625be..0affeb6799 100644 --- a/src/Umbraco.Web/Routing/RoutingEnvironment.cs +++ b/src/Umbraco.Web/Routing/RoutingEnvironment.cs @@ -3,10 +3,11 @@ using System.Linq; namespace Umbraco.Web.Routing { - // represents a request for one specified Umbraco document to be rendered - // by one specified template, using one particular culture. - // - + + /// + /// represents a request for one specified Umbraco document to be rendered by one specified template, + /// using one particular culture. + /// internal class RoutingEnvironment { public RoutingEnvironment( @@ -28,13 +29,9 @@ namespace Umbraco.Web.Routing }).ToList(); } - public IEnumerable Lookups - { - get; - private set; - } + public IEnumerable Lookups { get; private set; } - public ILookupNotFound LookupNotFound { get; private set; } + public ILookupNotFound LookupNotFound { get; private set; } public ContentStore ContentStore { get; private set; } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a9174e4cfb..b3ec0b677e 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -240,11 +240,12 @@ - + + @@ -252,6 +253,7 @@ + diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index 4992fa3e07..6d6729b7c7 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -49,7 +49,7 @@ namespace Umbraco.Web /// /// Gets the current Umbraco Context. /// - public static UmbracoContext Current + public static UmbracoContext Current { get { @@ -126,6 +126,16 @@ namespace Umbraco.Web /// internal DocumentRequest DocumentRequest { get; set; } + /// + /// Gets/sets the NiceUrlResolver object + /// + internal NiceUrlResolver NiceUrlResolver { get; set; } + + /// + /// Gets/sets the RoutingEnvironment object + /// + internal RoutingEnvironment RoutingEnvironment { get; set; } + /// /// Exposes the HttpContext for the current request /// @@ -193,29 +203,7 @@ namespace Umbraco.Web && !currentUrl.StartsWith(IOHelper.ResolveUrl(SystemDirectories.Umbraco)); // is not in admin UI } } - - /// - /// Gets the current Live Editing Context. - /// - public virtual ILiveEditingContext LiveEditingContext - { - get - { - //TODO: this should be done with a wrapper: http://issues.umbraco.org/issue/U4-61 - var value = (ILiveEditingContext)HttpContext.Items["LiveEditingContext"]; - if (value == null) - { - LiveEditingContext = value = new DefaultLiveEditingContext(); - } - return value; - } - - set - { - //TODO: this should be done with a wrapper: http://issues.umbraco.org/issue/U4-61 - HttpContext.Items["LiveEditingContext"] = value; - } - } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index d4b2ba0d35..1087e61fe3 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -37,11 +37,9 @@ namespace Umbraco.Web UmbracoContext.Current = umbracoContext; //create a content store - var contentStore = new ContentStore(umbracoContext); - //create the routes cache - var routesCache = new RoutesCache(umbracoContext); + var contentStore = new ContentStore(umbracoContext); //create the nice urls - var niceUrls = new NiceUrls(contentStore, umbracoContext, routesCache); + var niceUrls = new NiceUrlResolver(contentStore, umbracoContext, RoutesCache.Current.GetProvider()); //create the RoutingEnvironment (one per http request as it relies on the umbraco context!) var routingEnvironment = new RoutingEnvironment( ApplicationContext.Current.Plugins.ResolveLookups().ToArray(), @@ -49,9 +47,15 @@ namespace Umbraco.Web contentStore); // create the new document request which will cleanup the uri once and for all var docreq = new DocumentRequest(uri, routingEnvironment, umbracoContext, niceUrls); - - // initialize the document request on the UmbracoContext (this is circular dependency!!!) + + //NOTE: we are putting these objects on the UmbracoContext because these might be handy for developers in the future to + // access if we make them public. + // initialize the DocumentRequest on the UmbracoContext (this is circular dependency!!!) umbracoContext.DocumentRequest = docreq; + // initialize the RoutingEnvironment on the UmbracoContext (this is circular dependency!!!) + umbracoContext.RoutingEnvironment = routingEnvironment; + // initialize the RoutingEnvironment on the UmbracoContext (this is circular dependency!!!) + umbracoContext.NiceUrlResolver = niceUrls; //create the LegacyRequestInitializer (one per http request as it relies on the umbraco context!) var legacyRequestInitializer = new LegacyRequestInitializer(umbracoContext);