This commit is contained in:
Stephan
2012-07-20 19:09:30 -02:00
27 changed files with 1406 additions and 1404 deletions

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Web.Routing
{
/// <summary>
/// The default implementation of IRoutesCache
/// </summary>
internal class DefaultRoutesCache : IRoutesCache
{
private readonly object _lock = new object();
private Dictionary<int, string> _routes;
private Dictionary<string, int> _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<int, string>();
_nodeIds = new Dictionary<string, int>();
}
}
}
}

View File

@@ -6,6 +6,7 @@ using System.Globalization;
using System.Diagnostics;
// legacy
using umbraco.BusinessLogic;
using umbraco.cms.businesslogic.web;
using umbraco.cms.businesslogic.template;
using umbraco.cms.businesslogic.member;
@@ -19,21 +20,28 @@ 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, RoutingContext routingContext)
{
this.Uri = uri;
_environment = lookups;
_umbracoContext = umbracoContext;
_niceUrls = niceUrls;
RoutingContext = routingContext;
}
/// <summary>
/// the id of the requested node, if any, else zero.
/// </summary>
int _nodeId = 0;
/// <summary>
/// the requested node, if any, else null.
/// </summary>
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;
/// <summary>
/// Returns the current RoutingContext
/// </summary>
public RoutingContext RoutingContext { get; private set; }
public Uri Uri { get; private set; }
@@ -74,7 +82,7 @@ namespace Umbraco.Web.Routing
_node = value;
this.Template = null;
if (_node != null)
_nodeId = int.Parse(_environment.ContentStore.GetNodeProperty(_node, "@id"));
_nodeId = int.Parse(RoutingContext.ContentStore.GetNodeProperty(_node, "@id"));
else
_nodeId = 0;
}
@@ -134,15 +142,9 @@ namespace Umbraco.Web.Routing
#region Resolve
readonly RoutingEnvironment _environment;
private readonly UmbracoContext _umbracoContext;
private readonly NiceUrls _niceUrls;
/// <summary>
/// Determines the site root (if any) matching the http request.
/// </summary>
/// <param name="host">The host name part of the http request, eg. <c>www.example.com</c>.</param>
/// <param name="url">The url part of the http request, starting with a slash, eg. <c>/foo/bar</c>.</param>
/// </summary>
/// <returns>A value indicating whether a domain was found.</returns>
public bool ResolveDomain()
{
@@ -200,7 +202,8 @@ namespace Umbraco.Web.Routing
// the first successful lookup, if any, will set this.Node, and may also set this.Template
// some lookups may implement caching
Trace.TraceInformation("{0}Begin lookup", tracePrefix);
_environment.Lookups.Any(lookup => lookup.LookupDocument(this));
var lookups = RoutingContext.RouteLookups.GetLookups();
lookups.Any(lookup => lookup.LookupDocument(this));
Trace.TraceInformation("{0}End lookup, {1}", tracePrefix, (this.HasNode ? "a document was found" : "no document was found"));
// fixme - not handling umbracoRedirect
@@ -240,7 +243,7 @@ namespace Umbraco.Web.Routing
Trace.TraceInformation("{0}No document, try notFound lookup", tracePrefix);
// if it fails then give up, there isn't much more that we can do
if (_environment.LookupNotFound == null || !_environment.LookupNotFound.LookupDocument(this))
if (RoutingContext.LookupNotFound == null || !RoutingContext.LookupNotFound.LookupDocument(this))
{
Trace.TraceInformation("{0}Failed to find a document, give up", tracePrefix);
break;
@@ -292,7 +295,7 @@ namespace Umbraco.Web.Routing
throw new InvalidOperationException("There is no node.");
bool redirect = false;
string internalRedirect = _environment.ContentStore.GetNodeProperty(this.Node, "umbracoInternalRedirectId");
string internalRedirect = RoutingContext.ContentStore.GetNodeProperty(this.Node, "umbracoInternalRedirectId");
if (!string.IsNullOrWhiteSpace(internalRedirect))
{
@@ -316,7 +319,7 @@ namespace Umbraco.Web.Routing
else
{
// redirect to another page
var node = _environment.ContentStore.GetNodeById(internalRedirectId);
var node = RoutingContext.ContentStore.GetNodeById(internalRedirectId);
this.Node = node;
if (node != null)
{
@@ -344,7 +347,7 @@ namespace Umbraco.Web.Routing
if (this.Node == null)
throw new InvalidOperationException("There is no node.");
var path = _environment.ContentStore.GetNodeProperty(this.Node, "@path");
var path = RoutingContext.ContentStore.GetNodeProperty(this.Node, "@path");
if (Access.IsProtected(this.NodeId, path))
{
@@ -357,14 +360,14 @@ namespace Umbraco.Web.Routing
Trace.TraceInformation("{0}Not logged in, redirect to login page", tracePrefix);
var loginPageId = Access.GetLoginPage(path);
if (loginPageId != this.NodeId)
this.Node = _environment.ContentStore.GetNodeById(loginPageId);
this.Node = RoutingContext.ContentStore.GetNodeById(loginPageId);
}
else if (!Access.HasAccces(this.NodeId, user.ProviderUserKey))
{
Trace.TraceInformation("{0}Current member has not access, redirect to error page", tracePrefix);
var errorPageId = Access.GetErrorPage(path);
if (errorPageId != this.NodeId)
this.Node = _environment.ContentStore.GetNodeById(errorPageId);
this.Node = RoutingContext.ContentStore.GetNodeById(errorPageId);
}
else
{
@@ -387,9 +390,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 = RoutingContext.UmbracoContext.HttpContext.Request.QueryString["altTemplate"];
if (string.IsNullOrWhiteSpace(templateAlias))
templateAlias = _umbracoContext.HttpContext.Request.Form["altTemplate"];
templateAlias = RoutingContext.UmbracoContext.HttpContext.Request.Form["altTemplate"];
// fixme - we might want to support cookies?!? NO but provide a hook to change the template
@@ -397,7 +400,7 @@ namespace Umbraco.Web.Routing
{
if (string.IsNullOrWhiteSpace(templateAlias))
{
templateAlias = _environment.ContentStore.GetNodeProperty(this.Node, "@template");
templateAlias = RoutingContext.ContentStore.GetNodeProperty(this.Node, "@template");
Trace.TraceInformation("{0}Look for template id={1}", tracePrefix, templateAlias);
int templateId;
if (!int.TryParse(templateAlias, out templateId))
@@ -439,11 +442,11 @@ namespace Umbraco.Web.Routing
if (this.HasNode)
{
int redirectId;
if (!int.TryParse(_environment.ContentStore.GetNodeProperty(this.Node, "umbracoRedirect"), out redirectId))
if (!int.TryParse(RoutingContext.ContentStore.GetNodeProperty(this.Node, "umbracoRedirect"), out redirectId))
redirectId = -1;
string redirectUrl = "#";
if (redirectId > 0)
redirectUrl = _niceUrls.GetNiceUrl(redirectId);
redirectUrl = RoutingContext.NiceUrlResolver.GetNiceUrl(redirectId);
if (redirectUrl != "#")
this.RedirectUrl = redirectUrl;
}

View File

@@ -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();
}
}

View File

@@ -13,14 +13,9 @@ namespace Umbraco.Web.Routing
[LookupWeight(50)]
internal class LookupByAlias : ILookup
{
public LookupByAlias(ContentStore contentStore)
{
_contentStore = contentStore;
}
static readonly TraceSource Trace = new TraceSource("LookupByAlias");
readonly ContentStore _contentStore;
public bool LookupDocument(DocumentRequest docreq)
{
@@ -28,7 +23,7 @@ namespace Umbraco.Web.Routing
if (docreq.Uri.AbsolutePath != "/") // no alias if "/"
{
node = _contentStore.GetNodeByUrlAlias(docreq.HasDomain ? docreq.Domain.RootNodeId : 0, docreq.Uri.AbsolutePath);
node = docreq.RoutingContext.ContentStore.GetNodeByUrlAlias(docreq.HasDomain ? docreq.Domain.RootNodeId : 0, docreq.Uri.AbsolutePath);
if (node != null)
{
Trace.TraceInformation("Path \"{0}\" is an alias for id={1}", docreq.Uri.AbsolutePath, docreq.NodeId);

View File

@@ -10,24 +10,9 @@ namespace Umbraco.Web.Routing
[LookupWeight(20)]
internal class LookupById : ILookup
{
public LookupById(ContentStore contentStore)
{
_contentStore = contentStore;
}
static readonly TraceSource Trace = new TraceSource("LookupById");
readonly ContentStore _contentStore;
////[Import]
//IContentStore ContentStoreImport
//{
// set { _contentStore = value; }
//}
//public LookupById()
//{ }
public bool LookupDocument(DocumentRequest docreq)
{
XmlNode node = null;
@@ -43,7 +28,7 @@ namespace Umbraco.Web.Routing
if (nodeId > 0)
{
Trace.TraceInformation("Id={0}", nodeId);
node = _contentStore.GetNodeById(nodeId);
node = docreq.RoutingContext.ContentStore.GetNodeById(nodeId);
if (node != null)
{
docreq.Node = node;

View File

@@ -9,17 +9,9 @@ namespace Umbraco.Web.Routing
[LookupWeight(10)]
internal class LookupByPath : ILookup
{
public LookupByPath(ContentStore contentStore, RoutesCache routesCache)
{
ContentStore = contentStore;
RoutesCache = routesCache;
}
static readonly TraceSource Trace = new TraceSource("LookupByPath");
protected ContentStore ContentStore;
protected RoutesCache RoutesCache;
public virtual bool LookupDocument(DocumentRequest docreq)
{
string route;
@@ -36,11 +28,16 @@ namespace Umbraco.Web.Routing
{
Trace.TraceInformation("Test route \"{0}\"", route);
var nodeId = RoutesCache.GetNodeId(route);
//return '0' if in preview mode!
var nodeId = !docreq.RoutingContext.UmbracoContext.InPreviewMode
? docreq.RoutingContext.UmbracoContext.RoutesCache.GetNodeId(route)
: 0;
XmlNode node = null;
if (nodeId > 0)
{
node = ContentStore.GetNodeById(nodeId);
node = docreq.RoutingContext.ContentStore.GetNodeById(nodeId);
if (node != null)
{
docreq.Node = node;
@@ -48,19 +45,24 @@ namespace Umbraco.Web.Routing
}
else
{
RoutesCache.ClearNode(nodeId);
docreq.RoutingContext.UmbracoContext.RoutesCache.ClearNode(nodeId);
}
}
if (node == null)
{
Trace.TraceInformation("Cache miss, query");
node = ContentStore.GetNodeByRoute(route);
node = docreq.RoutingContext.ContentStore.GetNodeByRoute(route);
if (node != null)
{
docreq.Node = node;
Trace.TraceInformation("Query matches, id={0}", docreq.NodeId);
RoutesCache.Store(docreq.NodeId, route);
if (!docreq.RoutingContext.UmbracoContext.InPreviewMode)
{
docreq.RoutingContext.UmbracoContext.RoutesCache.Store(docreq.NodeId, route); // will not write if previewing
}
}
else
{

View File

@@ -11,12 +11,7 @@ namespace Umbraco.Web.Routing
[LookupWeight(30)]
internal class LookupByPathWithTemplate : LookupByPath, ILookup
{
static readonly TraceSource Trace = new TraceSource("LookupByPathWithTemplate");
public LookupByPathWithTemplate(ContentStore contentStore, RoutesCache routesCache)
: base(contentStore, routesCache)
{
}
static readonly TraceSource Trace = new TraceSource("LookupByPathWithTemplate");
public override bool LookupDocument(DocumentRequest docreq)
{

View File

@@ -14,15 +14,7 @@ namespace Umbraco.Web.Routing
[LookupWeight(40)]
internal class LookupByProfile : LookupByPath, ILookup
{
private readonly UmbracoContext _umbracoContext;
static readonly TraceSource Trace = new TraceSource("LookupByProfile");
public LookupByProfile(ContentStore contentStore, RoutesCache routesCache, UmbracoContext umbracoContext)
: base(contentStore, routesCache)
{
_umbracoContext = umbracoContext;
}
static readonly TraceSource Trace = new TraceSource("LookupByProfile");
public override bool LookupDocument(DocumentRequest docreq)
{
@@ -44,7 +36,10 @@ namespace Umbraco.Web.Routing
node = LookupDocumentNode(docreq, route);
if (node != null)
_umbracoContext.HttpContext.Items["umbMemberLogin"] = memberLogin;
{
//TODO: Should be handled by Context Items class manager (http://issues.umbraco.org/issue/U4-61)
docreq.RoutingContext.UmbracoContext.HttpContext.Items["umbMemberLogin"] = memberLogin;
}
else
Trace.TraceInformation("No document matching profile path?");
}

View File

@@ -11,18 +11,12 @@ namespace Umbraco.Web.Routing
{
internal class LookupFor404 : ILookupNotFound
{
public LookupFor404(ContentStore contentStore)
{
_contentStore = contentStore;
}
static TraceSource _trace = new TraceSource("LookupByAlias");
private readonly ContentStore _contentStore;
public bool LookupDocument(DocumentRequest docRequest)
{
docRequest.Node = HandlePageNotFound(docRequest.Uri.AbsolutePath);
docRequest.Node = HandlePageNotFound(docRequest);
return docRequest.HasNode;
}
@@ -30,17 +24,17 @@ namespace Umbraco.Web.Routing
// copied from presentation/requestHandler
// temporary!!
XmlNode HandlePageNotFound(string url)
XmlNode HandlePageNotFound(DocumentRequest docRequest)
{
HttpContext.Current.Trace.Write("NotFoundHandler", string.Format("Running for url='{0}'.", url));
HttpContext.Current.Trace.Write("NotFoundHandler", string.Format("Running for url='{0}'.", docRequest.Uri.AbsolutePath));
XmlNode currentPage = null;
foreach (var handler in GetNotFoundHandlers())
{
if (handler.Execute(url) && handler.redirectID > 0)
if (handler.Execute(docRequest.Uri.AbsolutePath) && handler.redirectID > 0)
{
//currentPage = umbracoContent.GetElementById(handler.redirectID.ToString());
currentPage = _contentStore.GetNodeById(handler.redirectID);
currentPage = docRequest.RoutingContext.ContentStore.GetNodeById(handler.redirectID);
// FIXME - could it be null?

View File

@@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Umbraco.Core;
namespace Umbraco.Web.Routing
{
/// <summary>
/// Represents a collection of ILookup used for routing that are registered in the application
/// </summary>
internal class RouteLookups
{
private static readonly List<ILookup> Lookups = new List<ILookup>();
private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();
/// <summary>
/// Singleton accessor
/// </summary>
public static RouteLookups Current { get; internal set; }
internal RouteLookups(IEnumerable<ILookup> lookups)
{
Lookups.AddRange(SortByWeight(lookups));
}
/// <summary>
/// Returns all of the lookups
/// </summary>
/// <returns></returns>
public IEnumerable<ILookup> GetLookups()
{
return Lookups;
}
/// <summary>
/// Removes an ILookup based on the specified Type
/// </summary>
/// <typeparam name="T"></typeparam>
public void RemoveLookup<T>()
where T : ILookup
{
using (new WriteLock(Lock))
{
Lookups.Remove(Lookups.SingleOrDefault(x => x is T));
}
}
/// <summary>
/// Adds a new lookup to the end of the list
/// </summary>
/// <param name="lookup"></param>
public void AddLookup(ILookup lookup)
{
if (CheckExists(lookup))
throw new InvalidOperationException("The lookup type " + lookup + " already exists in the lookup collection");
using (new WriteLock(Lock))
{
Lookups.Add(lookup);
}
}
/// <summary>
/// Inserts a lookup at the specified index
/// </summary>
/// <param name="index"></param>
/// <param name="lookup"></param>
public void InsertLookup(int index, ILookup lookup)
{
if (CheckExists(lookup))
throw new InvalidOperationException("The lookup type " + lookup + " already exists in the lookup collection");
using (new WriteLock(Lock))
{
Lookups.Insert(index, lookup);
}
}
/// <summary>
/// checks if a lookup already exists by type
/// </summary>
/// <param name="lookup"></param>
/// <returns></returns>
private static bool CheckExists(ILookup lookup)
{
return Lookups.Any(x => x.GetType() == lookup.GetType());
}
/// <summary>
/// Sorts the ILookups in the list based on an attribute weight if one is specified
/// </summary>
/// <param name="lookups"></param>
/// <returns></returns>
private static IEnumerable<ILookup> SortByWeight(IEnumerable<ILookup> lookups)
{
return lookups.OrderBy(x =>
{
var attribute = x.GetType().GetCustomAttributes(true).OfType<LookupWeightAttribute>().SingleOrDefault();
return attribute == null ? LookupWeightAttribute.DefaultWeight : attribute.Weight;
}).ToList();
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System;
namespace Umbraco.Web.Routing
{
@@ -8,82 +8,54 @@ namespace Umbraco.Web.Routing
//
// a route is [rootId]/path/to/node
// where rootId is the id of the "site root" node
// if missing then the "site root" is the content root
//
internal class RoutesCache
{
private readonly object _lock = new object();
private Dictionary<int, string> _routes;
private Dictionary<string, int> _nodeIds;
internal class RoutesCache
{
private static readonly RoutesCache Instance = new RoutesCache();
private static IRoutesCache _provider;
private readonly UmbracoContext _umbracoContext;
/// <summary>
/// public contructor assigns the DefaultRoutesCache as the default provider
/// </summary>
public RoutesCache()
:this(null)
{
}
public RoutesCache(UmbracoContext umbracoContext)
{
_umbracoContext = umbracoContext;
/// <summary>
/// Constructor sets a custom provider if specified
/// </summary>
/// <param name="provider"></param>
internal RoutesCache(IRoutesCache provider)
{
_provider = provider ?? new DefaultRoutesCache();
}
Clear();
/// <summary>
/// Set the routes cache provider
/// </summary>
/// <param name="provider"></param>
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...
}
/// <summary>
/// Singleton accessor
/// </summary>
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<int, string>();
_nodeIds = new Dictionary<string, int>();
}
}
}
/// <summary>
/// Get the current provider
/// </summary>
/// <returns></returns>
public IRoutesCache GetProvider()
{
return _provider;
}
}
}

View File

@@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.Linq;
namespace Umbraco.Web.Routing
{
/// <summary>
/// represents a request for one specified Umbraco document to be rendered by one specified template,
/// using one particular culture.
/// </summary>
internal class RoutingContext
{
public RoutingContext(
UmbracoContext umbracoContext,
RouteLookups lookups,
ILookupNotFound lookupNotFound,
ContentStore contentStore,
NiceUrlResolver niceUrlResolver)
{
UmbracoContext = umbracoContext;
RouteLookups = lookups;
LookupNotFound = lookupNotFound;
ContentStore = contentStore;
NiceUrlResolver = niceUrlResolver;
}
public UmbracoContext UmbracoContext { get; private set; }
public RouteLookups RouteLookups { get; private set; }
public ILookupNotFound LookupNotFound { get; private set; }
public ContentStore ContentStore { get; private set; }
public NiceUrlResolver NiceUrlResolver { get; private set; }
}
}

View File

@@ -1,38 +0,0 @@
using System.Collections.Generic;
using System.Linq;
namespace Umbraco.Web.Routing
{
internal class RoutingEnvironment
{
public RoutingEnvironment(
IEnumerable<ILookup> lookups,
ILookupNotFound lookupNotFound,
ContentStore contentStore)
{
Lookups = SortByPartWeight(lookups);
LookupNotFound = lookupNotFound;
ContentStore = contentStore;
}
private static IEnumerable<ILookup> SortByPartWeight(IEnumerable<ILookup> lookups)
{
return lookups.OrderBy(x =>
{
var attribute = x.GetType().GetCustomAttributes(true).OfType<LookupWeightAttribute>().SingleOrDefault();
return attribute == null ? LookupWeightAttribute.DefaultWeight : attribute.Weight;
}).ToList();
}
public IEnumerable<ILookup> Lookups
{
get;
private set;
}
public ILookupNotFound LookupNotFound { get; private set; }
public ContentStore ContentStore { get; private set; }
}
}