refactor routing
This commit is contained in:
@@ -5,18 +5,96 @@ namespace Umbraco.Core
|
||||
{
|
||||
internal static class UriExtensions
|
||||
{
|
||||
public static Uri Rewrite(this Uri uri, string path, string query)
|
||||
{
|
||||
var pathAndQuery = new StringBuilder();
|
||||
// Creates a new Uri with a rewritten path
|
||||
// Everything else is unchanged but for the fragment which is lost
|
||||
public static Uri Rewrite(this Uri uri, string path)
|
||||
{
|
||||
if (uri.IsAbsoluteUri)
|
||||
{
|
||||
return new Uri(uri.GetLeftPart(UriPartial.Authority) + path + uri.Query);
|
||||
}
|
||||
else
|
||||
{
|
||||
// cannot get .Query on relative uri (InvalidOperation)
|
||||
var s = uri.OriginalString;
|
||||
var posq = s.IndexOf("?");
|
||||
var posf = s.IndexOf("#");
|
||||
var query = posq < 0 ? null : (posf < 0 ? s.Substring(posq) : s.Substring(posq, posf - posq));
|
||||
|
||||
if (!path.StartsWith("/"))
|
||||
pathAndQuery.Append("/");
|
||||
pathAndQuery.Append(path);
|
||||
if (!query.StartsWith("?"))
|
||||
pathAndQuery.Append("?");
|
||||
pathAndQuery.Append(query);
|
||||
return new Uri(path + query, UriKind.Relative);
|
||||
}
|
||||
}
|
||||
|
||||
return new Uri(uri, pathAndQuery.ToString());
|
||||
}
|
||||
}
|
||||
// Creates a new Uri with a rewritten path and query
|
||||
// Everything else is unchanged but for the fragment which is lost
|
||||
public static Uri Rewrite(this Uri uri, string path, string query)
|
||||
{
|
||||
if (uri.IsAbsoluteUri)
|
||||
{
|
||||
return new Uri(uri.GetLeftPart(UriPartial.Authority) + path + query);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Uri(path + query, UriKind.Relative);
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the absolute path of the Uri
|
||||
// Works both for Absolute and Relative Uris
|
||||
public static string GetSafeAbsolutePath(this Uri uri)
|
||||
{
|
||||
if (uri.IsAbsoluteUri)
|
||||
{
|
||||
return uri.AbsolutePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
// cannot get .AbsolutePath on relative uri (InvalidOperation)
|
||||
var s = uri.OriginalString;
|
||||
var posq = s.IndexOf("?");
|
||||
var posf = s.IndexOf("#");
|
||||
var pos = posq > 0 ? posq : (posf > 0 ? posf : 0);
|
||||
var path = pos > 0 ? s.Substring(0, pos) : s;
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a new Uri with path ending with a slash
|
||||
// Everything else is unchanged but for the fragment which is lost
|
||||
public static Uri EndPathWithSlash(this Uri uri)
|
||||
{
|
||||
var path = uri.GetSafeAbsolutePath();
|
||||
if (uri.IsAbsoluteUri)
|
||||
{
|
||||
if (path != "/" && !path.EndsWith("/"))
|
||||
uri = new Uri(uri.GetLeftPart(UriPartial.Authority) + path + "/" + uri.Query);
|
||||
return uri;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (path != "/" && !path.EndsWith("/"))
|
||||
uri = new Uri(path + "/" + uri.Query, UriKind.Relative);
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
// Creates a new Uri with path trimmed of trailing slash
|
||||
// Everything else is unchanged but for the fragment which is lost
|
||||
// If path is "/" it remains "/".
|
||||
public static Uri TrimPathEndSlash(this Uri uri)
|
||||
{
|
||||
var path = uri.GetSafeAbsolutePath();
|
||||
if (uri.IsAbsoluteUri)
|
||||
{
|
||||
if (path != "/")
|
||||
uri = new Uri(uri.GetLeftPart(UriPartial.Authority) + path.TrimEnd('/') + uri.Query);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (path != "/")
|
||||
uri = new Uri(path.TrimEnd('/') + uri.Query, UriKind.Relative);
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,129 +23,123 @@ namespace Umbraco.Web
|
||||
// note: this could be a parameter...
|
||||
const string UrlNameProperty = "@urlName";
|
||||
|
||||
public virtual string GetNiceUrl(int nodeId)
|
||||
public string GetNiceUrl(int nodeId)
|
||||
{
|
||||
int startNodeDepth = 1;
|
||||
if (GlobalSettings.HideTopLevelNodeFromPath)
|
||||
startNodeDepth = 2;
|
||||
|
||||
return GetNiceUrl(nodeId, startNodeDepth, false);
|
||||
return GetNiceUrl(nodeId, FIXME*Current.UmbracoUrl, false);
|
||||
}
|
||||
|
||||
public virtual string GetNiceUrl(int nodeId, int startNodeDepth, bool forceDomain)
|
||||
public string GetNiceUrl(int nodeId, Uri current, bool absolute)
|
||||
{
|
||||
string route;
|
||||
string path;
|
||||
string path;
|
||||
Uri domainUri;
|
||||
|
||||
string route = _routesCache.GetRoute(nodeId); // will get null if previewing
|
||||
|
||||
route = _routesCache.GetRoute(nodeId); // will not read cache if previewing
|
||||
if (route != null)
|
||||
{
|
||||
// route is <id>/<path> eg "-1/", "-1/foo", "123/", "123/foo/bar"...
|
||||
int pos = route.IndexOf('/');
|
||||
path = route.Substring(pos);
|
||||
|
||||
if (UmbracoSettings.UseDomainPrefixes || forceDomain)
|
||||
{
|
||||
int rootNodeId = int.Parse(route.Substring(0, pos));
|
||||
if (rootNodeId > 0)
|
||||
return DomainAtNode(rootNodeId) + path;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
// else there was not route in the cache, must build route...
|
||||
|
||||
int id = int.Parse(route.Substring(0, pos)); // will be -1 or 1234
|
||||
domainUri = id > 0 ? DomainUriAtNode(id, current) : null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var node = _contentStore.GetNodeById(nodeId);
|
||||
if (node == null)
|
||||
return "#"; // legacy wrote to the log here...
|
||||
|
||||
var parts = new List<string>();
|
||||
var depth = int.Parse(_contentStore.GetNodeProperty(node, "@level"));
|
||||
var id = nodeId;
|
||||
string domain = null;
|
||||
while (depth >= 1)
|
||||
{
|
||||
// if not hiding that depth, add urlName
|
||||
if (depth >= startNodeDepth)
|
||||
parts.Add(_contentStore.GetNodeProperty(node, UrlNameProperty));
|
||||
|
||||
var tmp = DomainAtNode(id);
|
||||
if (tmp != null)
|
||||
{
|
||||
if (UmbracoSettings.UseDomainPrefixes || forceDomain)
|
||||
domain = tmp;
|
||||
break; // break to capture the id
|
||||
}
|
||||
return "#";
|
||||
|
||||
var pathParts = new List<string>();
|
||||
int id = nodeId;
|
||||
domainUri = DomainUriAtNode(id, current);
|
||||
while (domainUri == null && id > 0)
|
||||
{
|
||||
pathParts.Add(_contentStore.GetNodeProperty(node, UrlNameProperty));
|
||||
node = _contentStore.GetNodeParent(node);
|
||||
id = int.Parse(_contentStore.GetNodeProperty(node, "@id"));
|
||||
depth--;
|
||||
domainUri = id > 0 ? DomainUriAtNode(id, current) : null;
|
||||
}
|
||||
|
||||
parts.Reverse();
|
||||
path = "/" + string.Join("/", parts);
|
||||
route = string.Format("{0}{1}", id, path);
|
||||
// no domain, respect HideTopLevelNodeFromPath for legacy purposes
|
||||
if (domainUri == null && umbraco.GlobalSettings.HideTopLevelNodeFromPath)
|
||||
pathParts.RemoveAt(pathParts.Count - 1);
|
||||
|
||||
pathParts.Reverse();
|
||||
path = "/" + string.Join("/", pathParts); // will be "/" or "/foo" or "/foo/bar" etc
|
||||
route = id.ToString() + path;
|
||||
_routesCache.Store(nodeId, route); // will not write if previewing
|
||||
|
||||
return FormatUrl(domain, path);
|
||||
}
|
||||
|
||||
protected string DomainAtNode(int nodeId)
|
||||
{
|
||||
// be safe
|
||||
if (nodeId <= 0)
|
||||
return null;
|
||||
|
||||
// get domains defined on that node
|
||||
Domain[] domains = Domain.GetDomainsById(nodeId);
|
||||
|
||||
// no domain set on that node, return null
|
||||
if (domains.Length == 0)
|
||||
return null;
|
||||
|
||||
// else try to find the first domain that matches the current request
|
||||
// else take the first domain of the list
|
||||
Domain domain = domains.FirstOrDefault(d => UrlUtility.IsBaseOf(d.Name, _umbracoContext.OriginalUrl)) ?? domains[0];
|
||||
|
||||
var domainName = domain.Name.TrimEnd('/');
|
||||
domainName = UrlUtility.EnsureScheme(domainName, _umbracoContext.OriginalUrl.Scheme);
|
||||
var pos = domainName.IndexOf("//");
|
||||
pos = domainName.IndexOf("/", pos + 2);
|
||||
if (pos > 0)
|
||||
domainName = domainName.Substring(0, pos);
|
||||
|
||||
// return a scheme + host eg http://example.com with no trailing slash
|
||||
return domainName;
|
||||
return AssembleUrl(domainUri, path, current, absolute).ToString();
|
||||
}
|
||||
|
||||
protected string FormatUrl(string domain, string path)
|
||||
{
|
||||
if (domain == null) // else vdir needs to be in the domain
|
||||
{
|
||||
// get the application virtual dir (empty if no vdir)
|
||||
string vdir = SystemDirectories.Root;
|
||||
if (!string.IsNullOrEmpty(vdir))
|
||||
domain = "/" + vdir;
|
||||
}
|
||||
Uri AssembleUrl(Uri domain, string path, Uri current, bool absolute)
|
||||
{
|
||||
Uri uri;
|
||||
|
||||
string url = (domain ?? "") + path;
|
||||
if (path != "/")
|
||||
{
|
||||
// not at root
|
||||
if (GlobalSettings.UseDirectoryUrls)
|
||||
{
|
||||
// add trailing / if required
|
||||
if (UmbracoSettings.AddTrailingSlash)
|
||||
url += "/";
|
||||
}
|
||||
else
|
||||
{
|
||||
// add .aspx
|
||||
url += ".aspx";
|
||||
}
|
||||
}
|
||||
if (domain == null)
|
||||
{
|
||||
// no domain was found : return a relative url, add vdir if any
|
||||
uri = new Uri(umbraco.IO.SystemDirectories.Root + path, UriKind.Relative);
|
||||
}
|
||||
else
|
||||
{
|
||||
// a domain was found : return an absolute or relative url
|
||||
// cannot handle vdir, has to be in domain uri
|
||||
if (!absolute && current != null && domain.GetLeftPart(UriPartial.Authority) == current.GetLeftPart(UriPartial.Authority))
|
||||
uri = new Uri(domain.AbsolutePath.TrimEnd('/') + path, UriKind.Relative); // relative
|
||||
else
|
||||
uri = new Uri(domain.GetLeftPart(UriPartial.Path).TrimEnd('/') + path); // absolute
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
return UriFromUmbraco(uri);
|
||||
}
|
||||
|
||||
Uri DomainUriAtNode(int nodeId, Uri current)
|
||||
{
|
||||
// be safe
|
||||
if (nodeId <= 0)
|
||||
return null;
|
||||
|
||||
// apply filter on domains defined on that node
|
||||
var domainAndUri = Domains.ApplicableDomains(Domain.GetDomainsById(nodeId), current, true);
|
||||
return domainAndUri == null ? null : domainAndUri.Uri;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Map public urls to/from umbraco urls
|
||||
|
||||
// fixme - what about vdir?
|
||||
// path = path.Substring(UriUtility.AppVirtualPathPrefix.Length); // remove virtual directory
|
||||
|
||||
public static Uri UriFromUmbraco(Uri uri)
|
||||
{
|
||||
var path = uri.GetSafeAbsolutePath();
|
||||
if (path == "/")
|
||||
return uri;
|
||||
|
||||
if (!umbraco.GlobalSettings.UseDirectoryUrls)
|
||||
path += ".aspx";
|
||||
else if (umbraco.UmbracoSettings.AddTrailingSlash)
|
||||
path += "/";
|
||||
|
||||
return uri.Rewrite(path);
|
||||
}
|
||||
|
||||
public static Uri UriToUmbraco(Uri uri)
|
||||
{
|
||||
var path = uri.GetSafeAbsolutePath();
|
||||
|
||||
path = path.ToLower();
|
||||
if (path != "/")
|
||||
path = path.TrimEnd('/');
|
||||
if (path.EndsWith(".aspx"))
|
||||
path = path.Substring(0, path.Length - ".aspx".Length);
|
||||
|
||||
return uri.Rewrite(path);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -12,47 +12,19 @@ using umbraco.cms.businesslogic.member;
|
||||
using umbraco.cms.businesslogic.language;
|
||||
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
|
||||
{
|
||||
static readonly TraceSource Trace = new TraceSource("DocumentRequest");
|
||||
|
||||
public DocumentRequest(Uri uri, RoutingEnvironment lookups, UmbracoContext umbracoContext, NiceUrls niceUrls)
|
||||
{
|
||||
// register lookups
|
||||
this.Uri = uri;
|
||||
_environment = lookups;
|
||||
_umbracoContext = umbracoContext;
|
||||
_niceUrls = niceUrls;
|
||||
|
||||
// prepare the host
|
||||
var host = uri.Host;
|
||||
|
||||
// fixme
|
||||
//var serverVarHost = httpContext.Request.ServerVariables["X_UMBRACO_HOST"];
|
||||
//if (!string.IsNullOrWhiteSpace(serverVarHost))
|
||||
//{
|
||||
// host = serverVarHost;
|
||||
// RequestContext.Current.Trace.Write(TraceCategory, "Domain='" + host + "' (X_UMBRACO_HOST)");
|
||||
//}
|
||||
|
||||
// initialize the host
|
||||
this.Host = host;
|
||||
|
||||
// prepare the path
|
||||
var path = uri.AbsolutePath;
|
||||
path = path.Substring(UrlUtility.AppVirtualPathPrefix.Length); // remove virtual directory
|
||||
path = path.TrimEnd('/'); // remove trailing /
|
||||
if (!path.StartsWith("/")) // ensure it starts with /
|
||||
path = "/" + path;
|
||||
path = path.ToLower(); // make it all lowercase
|
||||
//url = url.Replace('\'', '_'); // make it xpath compatible !! was in legacy, should be handled in xpath query, not here
|
||||
if (path.EndsWith(".aspx")) // remove trailing .aspx
|
||||
path = path.Substring(0, path.Length - ".aspx".Length);
|
||||
|
||||
// initialize the path
|
||||
this.Path = path;
|
||||
|
||||
// initialize the query
|
||||
this.QueryString = uri.Query.TrimStart('?');
|
||||
}
|
||||
|
||||
#region Properties
|
||||
@@ -63,29 +35,15 @@ namespace Umbraco.Web.Routing
|
||||
// the requested node, if any, else null.
|
||||
XmlNode _node = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request host name.
|
||||
/// </summary>
|
||||
/// <remarks>This is the original uri's host, unless modified (fixme).</remarks>
|
||||
public string Host { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request path.
|
||||
/// </summary>
|
||||
/// <remarks>This is the original uri's path, cleaned up, without vdir, without .aspx, etc.</remarks>
|
||||
public string Path { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request query string.
|
||||
/// </summary>
|
||||
/// <remarks>This is the original uri's querystring, without the initial '?'.</remarks>
|
||||
public string QueryString { get; private set; }
|
||||
public Uri Uri { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the document request's domain.
|
||||
/// </summary>
|
||||
public Domain Domain { get; private set; }
|
||||
|
||||
public Uri DomainUri { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the document request has a domain.
|
||||
/// </summary>
|
||||
@@ -186,38 +144,35 @@ namespace Umbraco.Web.Routing
|
||||
/// <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>
|
||||
/// <returns>A value indicating whether a domain was found.</returns>
|
||||
public bool ResolveSiteRoot()
|
||||
public bool ResolveDomain()
|
||||
{
|
||||
const string tracePrefix = "ResolveSiteRoot: ";
|
||||
const string tracePrefix = "ResolveDomain: ";
|
||||
|
||||
// note - we are not handling schemes nor ports here.
|
||||
|
||||
Trace.TraceInformation("{0}Host=\"{1}\"", tracePrefix, this.Host);
|
||||
|
||||
Domain domain = null;
|
||||
|
||||
// get domains, those with a slash coming first, so that 'foo.com/en' takes precedence over 'foo.com'.
|
||||
// domains should NOT begin with 'http://'.
|
||||
var domains = Domain.GetDomains().OrderByDescending(od => od.Name.IndexOf('/'));
|
||||
Trace.TraceInformation("{0}Uri=\"{1}\"", tracePrefix, this.Uri);
|
||||
|
||||
// try to find a domain matching the current request
|
||||
string urlWithDomain = UrlUtility.EnsureScheme(this.Host + this.Path, "http"); // FIXME-NICEURL - current Uri?!
|
||||
domain = domains.FirstOrDefault(d => UrlUtility.IsBaseOf(d.Name, urlWithDomain));
|
||||
var domainAndUri = Domains.ApplicableDomains(Domain.GetDomains(), RequestContext.Current.UmbracoUrl, false);
|
||||
|
||||
// handle domain
|
||||
if (domain != null)
|
||||
if (domainAndUri != null)
|
||||
{
|
||||
// matching an existing domain
|
||||
Trace.TraceInformation("{0}Matches domain=\"{1}\", rootId={2}, culture=\"{3}\"",
|
||||
tracePrefix,
|
||||
domain.Name, domain.RootNodeId, domain.Language.CultureAlias);
|
||||
domainAndUri.Domain.Name, domainAndUri.Domain.RootNodeId, domainAndUri.Domain.Language.CultureAlias);
|
||||
|
||||
this.Domain = domain;
|
||||
this.Culture = new CultureInfo(domain.Language.CultureAlias);
|
||||
this.Domain = domainAndUri.Domain;
|
||||
this.DomainUri = domainAndUri.Uri;
|
||||
this.Culture = new CultureInfo(domainAndUri.Domain.Language.CultureAlias);
|
||||
|
||||
// canonical?
|
||||
// FIXME - NOT IMPLEMENTED AT THE MOMENT + THEN WE WOULD RETURN THE CANONICAL DOMAIN AND ASK FOR REDIRECT
|
||||
// but then how do I translate if domain is bar.com/en ? will depend on how we handle domains
|
||||
// canonical? not implemented at the moment
|
||||
// if (...)
|
||||
// {
|
||||
// this.RedirectUrl = "...";
|
||||
// return true;
|
||||
// }
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -239,7 +194,7 @@ namespace Umbraco.Web.Routing
|
||||
public bool ResolveDocument()
|
||||
{
|
||||
const string tracePrefix = "ResolveDocument: ";
|
||||
Trace.TraceInformation("{0}Path=\"{1}\"", tracePrefix, this.Path);
|
||||
Trace.TraceInformation("{0}Path=\"{1}\"", tracePrefix, this.Uri.AbsolutePath);
|
||||
|
||||
// look for the document
|
||||
// the first successful lookup, if any, will set this.Node, and may also set this.Template
|
||||
|
||||
64
src/Umbraco.Web/Routing/Domains.cs
Normal file
64
src/Umbraco.Web/Routing/Domains.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using umbraco.cms.businesslogic.web;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
public class Domains
|
||||
{
|
||||
public class DomainAndUri
|
||||
{
|
||||
public Domain Domain;
|
||||
public Uri Uri;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{{ \"{0}\", \"{1}\" }}", Domain.Name, Uri);
|
||||
}
|
||||
}
|
||||
|
||||
public static DomainAndUri ApplicableDomains(IEnumerable<Domain> domains, Uri current, bool defaultToFirst)
|
||||
{
|
||||
if (!domains.Any())
|
||||
return null;
|
||||
|
||||
// sanitize the list to have proper uris for comparison (scheme, path end with /)
|
||||
// we need to end with / because example.com/foo cannot match example.com/foobar
|
||||
// we need to order so example.com/foo matches before example.com/
|
||||
var scheme = current == null ? Uri.UriSchemeHttp : current.Scheme;
|
||||
var domainsAndUris = domains
|
||||
.Select(d => new { Domain = d, UriString = UriUtility.EndPathWithSlash(UriUtility.StartWithScheme(d.Name, scheme)) })
|
||||
.OrderByDescending(t => t.UriString)
|
||||
.Select(t => new DomainAndUri { Domain = t.Domain, Uri = new Uri(t.UriString) });
|
||||
|
||||
DomainAndUri domainAndUri;
|
||||
if (current == null)
|
||||
{
|
||||
// take the first one by default
|
||||
domainAndUri = domainsAndUris.First();
|
||||
}
|
||||
else
|
||||
{
|
||||
// look for a domain that would be the base of the hint
|
||||
// else take the first one by default
|
||||
var hintWithSlash = current.EndPathWithSlash();
|
||||
domainAndUri = domainsAndUris
|
||||
.FirstOrDefault(t => t.Uri.IsBaseOf(hintWithSlash));
|
||||
if (domainAndUri == null && defaultToFirst)
|
||||
domainAndUri = domainsAndUris.First();
|
||||
}
|
||||
|
||||
if (domainAndUri != null)
|
||||
domainAndUri.Uri = domainAndUri.Uri.TrimPathEndSlash();
|
||||
return domainAndUri;
|
||||
}
|
||||
|
||||
public static string PathRelativeToDomain(Uri domainUri, string path)
|
||||
{
|
||||
return path.Substring(domainUri.AbsolutePath.Length).AtStart('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,12 +26,12 @@ namespace Umbraco.Web.Routing
|
||||
{
|
||||
XmlNode node = null;
|
||||
|
||||
if (docreq.Path != "/") // no alias if "/"
|
||||
if (docreq.Uri.AbsolutePath != "/") // no alias if "/"
|
||||
{
|
||||
node = _contentStore.GetNodeByUrlAlias(docreq.HasDomain ? docreq.Domain.RootNodeId : 0, docreq.Path);
|
||||
node = _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.Path, docreq.NodeId);
|
||||
Trace.TraceInformation("Path \"{0}\" is an alias for id={1}", docreq.Uri.AbsolutePath, docreq.NodeId);
|
||||
docreq.Node = node;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,9 +33,9 @@ namespace Umbraco.Web.Routing
|
||||
XmlNode node = null;
|
||||
|
||||
int nodeId = -1;
|
||||
if (docreq.Path != "/") // no id if "/"
|
||||
if (docreq.Uri.AbsolutePath != "/") // no id if "/"
|
||||
{
|
||||
string noSlashPath = docreq.Path.Substring(1);
|
||||
string noSlashPath = docreq.Uri.AbsolutePath.Substring(1);
|
||||
|
||||
if (!Int32.TryParse(noSlashPath, out nodeId))
|
||||
nodeId = -1;
|
||||
|
||||
@@ -22,7 +22,12 @@ namespace Umbraco.Web.Routing
|
||||
|
||||
public virtual bool LookupDocument(DocumentRequest docreq)
|
||||
{
|
||||
var route = docreq.HasDomain ? (docreq.Domain.RootNodeId.ToString() + docreq.Path) : docreq.Path;
|
||||
string route;
|
||||
if (docreq.HasDomain)
|
||||
route = docreq.Domain.RootNodeId.ToString() + Domains.PathRelativeToDomain(docreq.DomainUri, docreq.Uri.AbsolutePath);
|
||||
else
|
||||
route = docreq.Uri.AbsolutePath;
|
||||
|
||||
var node = LookupDocumentNode(docreq, route);
|
||||
return node != null;
|
||||
}
|
||||
|
||||
@@ -21,12 +21,15 @@ namespace Umbraco.Web.Routing
|
||||
public override bool LookupDocument(DocumentRequest docreq)
|
||||
{
|
||||
XmlNode node = null;
|
||||
string path = docreq.Uri.AbsolutePath;
|
||||
|
||||
if (docreq.Path != "/") // no template if "/"
|
||||
if (docreq.HasDomain)
|
||||
path = Domains.PathRelativeToDomain(docreq.DomainUri, path);
|
||||
if (path != "/") // no template if "/"
|
||||
{
|
||||
var pos = docreq.Path.LastIndexOf('/');
|
||||
var templateAlias = docreq.Path.Substring(pos + 1);
|
||||
var path = docreq.Path.Substring(0, pos);
|
||||
var pos = docreq.Uri.AbsolutePath.LastIndexOf('/');
|
||||
var templateAlias = docreq.Uri.AbsolutePath.Substring(pos + 1);
|
||||
path = path.Substring(0, pos);
|
||||
|
||||
var template = Template.GetByAlias(templateAlias);
|
||||
if (template != null)
|
||||
|
||||
@@ -29,11 +29,11 @@ namespace Umbraco.Web.Routing
|
||||
XmlNode node = null;
|
||||
|
||||
bool isProfile = false;
|
||||
var pos = docreq.Path.LastIndexOf('/');
|
||||
var pos = docreq.Uri.AbsolutePath.LastIndexOf('/');
|
||||
if (pos > 0)
|
||||
{
|
||||
var memberLogin = docreq.Path.Substring(pos + 1);
|
||||
var path = docreq.Path.Substring(0, pos);
|
||||
var memberLogin = docreq.Uri.AbsolutePath.Substring(pos + 1);
|
||||
var path = docreq.Uri.AbsolutePath.Substring(0, pos);
|
||||
|
||||
if (path == GlobalSettings.ProfileUrl)
|
||||
{
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Umbraco.Web.Routing
|
||||
|
||||
public bool LookupDocument(DocumentRequest docRequest)
|
||||
{
|
||||
docRequest.Node = HandlePageNotFound(docRequest.Path);
|
||||
docRequest.Node = HandlePageNotFound(docRequest.Uri.AbsolutePath);
|
||||
return docRequest.HasNode;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,14 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
// this is a bi-directional cache that contains
|
||||
// - nodeId to route (used for NiceUrl)
|
||||
// - route to nodeId (used for inbound requests)
|
||||
//
|
||||
// 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();
|
||||
|
||||
@@ -3,10 +3,6 @@ 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.
|
||||
//
|
||||
|
||||
internal class RoutingEnvironment
|
||||
{
|
||||
public RoutingEnvironment(
|
||||
|
||||
@@ -36,6 +36,9 @@ namespace Umbraco.Web
|
||||
var umbracoContext = new UmbracoContext(new HttpContextWrapper(httpContext), ApplicationContext.Current);
|
||||
UmbracoContext.Current = umbracoContext;
|
||||
|
||||
// NO!
|
||||
// these are application-wide singletons!
|
||||
|
||||
//create a content store
|
||||
var contentStore = new ContentStore(umbracoContext);
|
||||
//create the routes cache
|
||||
@@ -47,7 +50,10 @@ namespace Umbraco.Web
|
||||
ApplicationContext.Current.Plugins.ResolveLookups().ToArray(),
|
||||
new LookupFor404(contentStore),
|
||||
contentStore);
|
||||
// create the new document request which will cleanup the uri once and for all
|
||||
|
||||
// NOT HERE BUT SEE **THERE** BELOW
|
||||
|
||||
// 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!!!)
|
||||
@@ -88,7 +94,9 @@ namespace Umbraco.Web
|
||||
// legacy - no idea what this is
|
||||
LegacyCleanUmbPageFromQueryString(ref uri, ref lpath);
|
||||
|
||||
docreq.ResolveSiteRoot();
|
||||
//**THERE** we should create the doc request
|
||||
// before, we're not sure we handling a doc request
|
||||
docreq.ResolveDomain();
|
||||
if (docreq.IsRedirect)
|
||||
httpContext.Response.Redirect(docreq.RedirectUrl, true);
|
||||
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = docreq.Culture;
|
||||
@@ -99,9 +107,8 @@ namespace Umbraco.Web
|
||||
if (docreq.Is404)
|
||||
httpContext.Response.StatusCode = 404;
|
||||
|
||||
// fixme - should use an IComponent here, so that we could hand the request over to MVC
|
||||
Trace.TraceInformation("Transfer to UmbracoDefault (default.aspx)");
|
||||
TransferRequest("~/default.aspx?" + docreq.QueryString);
|
||||
TransferRequest("~/default.aspx" + docreq.Uri.Query);
|
||||
|
||||
// it is up to default.aspx to figure out what to display in case
|
||||
// there is no document (ugly 404 page?) or no template (blank page?)
|
||||
@@ -163,7 +170,7 @@ namespace Umbraco.Web
|
||||
string bootUrl = null;
|
||||
if (UmbracoSettings.EnableSplashWhileLoading) // legacy - should go
|
||||
{
|
||||
string configPath = UrlUtility.ToAbsolute(SystemDirectories.Config);
|
||||
string configPath = UriUtility.ToAbsolute(SystemDirectories.Config);
|
||||
bootUrl = string.Format("{0}/splashes/booting.aspx?url={1}", configPath, HttpUtility.UrlEncode(uri.ToString()));
|
||||
// fixme ?orgurl=... ?retry=...
|
||||
}
|
||||
@@ -171,14 +178,14 @@ namespace Umbraco.Web
|
||||
//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.
|
||||
//else if (!string.IsNullOrWhiteSpace(Settings.BootSplashPage))
|
||||
//{
|
||||
// bootUrl = UrlUtility.ToAbsolute(Settings.BootSplashPage);
|
||||
// bootUrl = UriUtility.ToAbsolute(Settings.BootSplashPage);
|
||||
//}
|
||||
|
||||
else
|
||||
{
|
||||
// fixme - default.aspx has to be ready for RequestContext.DocumentRequest==null
|
||||
// fixme - in fact we should transfer to an empty html page...
|
||||
bootUrl = UrlUtility.ToAbsolute("~/default.aspx");
|
||||
bootUrl = UriUtility.ToAbsolute("~/default.aspx");
|
||||
}
|
||||
TransferRequest(bootUrl);
|
||||
return false;
|
||||
@@ -195,7 +202,7 @@ namespace Umbraco.Web
|
||||
if (!ApplicationContext.Current.IsConfigured)
|
||||
{
|
||||
Trace.TraceEvent(TraceEventType.Warning, 0, "Umbraco is not configured");
|
||||
string installPath = UrlUtility.ToAbsolute(SystemDirectories.Install);
|
||||
string installPath = UriUtility.ToAbsolute(SystemDirectories.Install);
|
||||
string installUrl = string.Format("{0}/default.aspx?redir=true&url={1}", installPath, HttpUtility.UrlEncode(uri.ToString()));
|
||||
httpContext.Response.Redirect(installUrl, true);
|
||||
return false;
|
||||
@@ -213,7 +220,7 @@ namespace Umbraco.Web
|
||||
// by clean WebAPI.
|
||||
|
||||
// fixme - do it once when initializing the module
|
||||
string baseUrl = UrlUtility.ToAbsolute(SystemDirectories.Base).ToLower();
|
||||
string baseUrl = UriUtility.ToAbsolute(SystemDirectories.Base).ToLower();
|
||||
if (!baseUrl.EndsWith("/"))
|
||||
baseUrl += "/";
|
||||
if (lpath.StartsWith(baseUrl))
|
||||
@@ -292,8 +299,6 @@ namespace Umbraco.Web
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region IHttpModule
|
||||
|
||||
@@ -4,24 +4,27 @@ using System.Web;
|
||||
|
||||
namespace Umbraco.Web
|
||||
{
|
||||
static class UrlUtility
|
||||
static class UriUtility
|
||||
{
|
||||
static readonly string _appVirtualPath;
|
||||
static readonly string _appVirtualPathPrefix;
|
||||
|
||||
static UrlUtility()
|
||||
static UriUtility()
|
||||
{
|
||||
// Virtual path
|
||||
_appVirtualPath = HttpRuntime.AppDomainAppVirtualPath ?? "/";
|
||||
_appVirtualPathPrefix = _appVirtualPath;
|
||||
if (_appVirtualPathPrefix == "/")
|
||||
_appVirtualPathPrefix = string.Empty;
|
||||
}
|
||||
|
||||
// will be "/" or "/foo"
|
||||
public static string AppVirtualPath
|
||||
{
|
||||
get { return _appVirtualPath; }
|
||||
}
|
||||
|
||||
// will be "" or "/foo"
|
||||
public static string AppVirtualPathPrefix
|
||||
{
|
||||
get { return _appVirtualPathPrefix; }
|
||||
@@ -104,42 +107,53 @@ namespace Umbraco.Web
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
#region Uri string utilities
|
||||
|
||||
public static bool HasScheme(string uri)
|
||||
{
|
||||
return uri.StartsWith("http://", StringComparison.CurrentCultureIgnoreCase)
|
||||
|| uri.StartsWith("https://", StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
public static bool HasScheme(string uri)
|
||||
{
|
||||
return uri.IndexOf("://") > 0;
|
||||
}
|
||||
|
||||
public static string EnsureScheme(string uri, string scheme)
|
||||
{
|
||||
return HasScheme(uri) ? uri : string.Format("{0}://{1}", scheme, uri);
|
||||
}
|
||||
public static string StartWithScheme(string uri)
|
||||
{
|
||||
return StartWithScheme(uri, null);
|
||||
}
|
||||
|
||||
public static string WithTrailingSlash(string uri)
|
||||
{
|
||||
return uri.EndsWith("/") ? uri : uri + "/";
|
||||
}
|
||||
public static string StartWithScheme(string uri, string scheme)
|
||||
{
|
||||
return HasScheme(uri) ? uri : string.Format("{0}://{1}", scheme ?? Uri.UriSchemeHttp, uri);
|
||||
}
|
||||
|
||||
// indicates whether uri2 is within uri1
|
||||
public static bool IsBaseOf(string uri1, string uri2)
|
||||
{
|
||||
uri2 = WithTrailingSlash(uri2);
|
||||
Uri testUri2 = new Uri(uri2);
|
||||
public static string EndPathWithSlash(string uri)
|
||||
{
|
||||
var pos1 = Math.Max(0, uri.IndexOf('?'));
|
||||
var pos2 = Math.Max(0, uri.IndexOf('#'));
|
||||
var pos = Math.Min(pos1, pos2);
|
||||
|
||||
uri1 = WithTrailingSlash(uri1);
|
||||
uri1 = EnsureScheme(uri1, testUri2.Scheme);
|
||||
Uri testUri1 = new Uri(uri1);
|
||||
var path = pos > 0 ? uri.Substring(0, pos) : uri;
|
||||
path = path.AtEnd('/');
|
||||
|
||||
return testUri1.IsBaseOf(testUri2);
|
||||
}
|
||||
if (pos > 0)
|
||||
path += uri.Substring(pos);
|
||||
|
||||
public static bool IsBaseOf(string uri1, Uri uri2)
|
||||
{
|
||||
return IsBaseOf(uri1, uri2.ToString());
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
#endregion
|
||||
public static string TrimPathEndSlash(string uri)
|
||||
{
|
||||
var pos1 = Math.Max(0, uri.IndexOf('?'));
|
||||
var pos2 = Math.Max(0, uri.IndexOf('#'));
|
||||
var pos = Math.Min(pos1, pos2);
|
||||
|
||||
var path = pos > 0 ? uri.Substring(0, pos) : uri;
|
||||
path = path.TrimEnd('/');
|
||||
|
||||
if (pos > 0)
|
||||
path += uri.Substring(pos);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -43,15 +43,6 @@ namespace umbraco
|
||||
/// </summary>
|
||||
public class library
|
||||
{
|
||||
internal static void ClearNiceUrlCache()
|
||||
{
|
||||
lock (locker)
|
||||
{
|
||||
niceUrlCache.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static object locker = new object();
|
||||
|
||||
#region Declarations
|
||||
|
||||
@@ -364,18 +355,7 @@ namespace umbraco
|
||||
/// <returns>String with a friendly url from a node</returns>
|
||||
public static string NiceUrl(int nodeID)
|
||||
{
|
||||
try
|
||||
{
|
||||
int startNode = 1;
|
||||
if (GlobalSettings.HideTopLevelNodeFromPath)
|
||||
startNode = 2;
|
||||
|
||||
return niceUrlDo(nodeID, startNode, false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "#";
|
||||
}
|
||||
return Umbraco.Core.UmbracoContainer.Get<Umbraco.Web.Routing.NiceUrls>().GetNiceUrl(nodeID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -384,9 +364,10 @@ namespace umbraco
|
||||
/// </summary>
|
||||
/// <param name="nodeID">Identifier for the node that should be returned</param>
|
||||
/// <returns>String with a friendly url from a node</returns>
|
||||
[Obsolete]
|
||||
public static string NiceUrlFullPath(int nodeID)
|
||||
{
|
||||
return niceUrlDo(nodeID, 1, false);
|
||||
throw new NotImplementedException("It was broken anyway...");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -396,7 +377,7 @@ namespace umbraco
|
||||
/// <returns>String with a friendly url with full domain from a node</returns>
|
||||
public static string NiceUrlWithDomain(int nodeID)
|
||||
{
|
||||
return niceUrlDo(nodeID, 1, true);
|
||||
return Umbraco.Core.UmbracoContainer.Get<Umbraco.Web.Routing.NiceUrls>().GetNiceUrl(nodeID, Umbraco.Web.RequestContext.Current.UmbracoUrl, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -405,144 +386,6 @@ namespace umbraco
|
||||
return IOHelper.ResolveUrl(path);
|
||||
}
|
||||
|
||||
private static IDictionary<int, string> niceUrlCache = new Dictionary<int, string>();
|
||||
|
||||
/// <summary>
|
||||
/// This is used in the requesthandler for domain lookups to ensure that we don't use the cache
|
||||
/// </summary>
|
||||
/// <param name="nodeID"></param>
|
||||
/// <param name="startNodeDepth"></param>
|
||||
/// <param name="byPassCache"></param>
|
||||
/// <returns></returns>
|
||||
private static string niceUrlDo(int nodeID, int startNodeDepth, bool forceDomain)
|
||||
{
|
||||
int key = nodeID + (forceDomain ? 9999999 : 0);
|
||||
if (!niceUrlCache.ContainsKey(key))
|
||||
{
|
||||
lock (locker)
|
||||
{
|
||||
if (!niceUrlCache.ContainsKey(key))
|
||||
{
|
||||
string tempUrl = NiceUrlFetch(nodeID, startNodeDepth, forceDomain);
|
||||
if (!String.IsNullOrEmpty(tempUrl))
|
||||
{
|
||||
niceUrlCache.Add(key, tempUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return niceUrlCache[key];
|
||||
}
|
||||
|
||||
internal static string niceUrlJuno(int nodeId, int startNodeDepth, string currentDomain, bool forceDomain)
|
||||
{
|
||||
string parentUrl = String.Empty;
|
||||
XmlElement node = UmbracoContext.Current.GetXml().GetElementById(nodeId.ToString());
|
||||
|
||||
if (node == null)
|
||||
{
|
||||
ArgumentException arEx =
|
||||
new ArgumentException(
|
||||
string.Format(
|
||||
"Couldn't find any page with the nodeId = {0}. This is most likely caused by the page isn't published!",
|
||||
nodeId), "nodeId");
|
||||
Log.Add(LogTypes.Error, nodeId, arEx.Message);
|
||||
throw arEx;
|
||||
}
|
||||
if (node.ParentNode.Name.ToLower() != "root" || UmbracoSettings.UseDomainPrefixes || forceDomain)
|
||||
{
|
||||
if (UmbracoSettings.UseDomainPrefixes || forceDomain)
|
||||
{
|
||||
Domain[] domains =
|
||||
Domain.GetDomainsById(nodeId);
|
||||
// when there's a domain on a url we'll just return the domain rather than the parent path
|
||||
if (domains.Length > 0)
|
||||
{
|
||||
return GetDomainIfExists(currentDomain, nodeId);
|
||||
}
|
||||
}
|
||||
|
||||
if (parentUrl == String.Empty && (int.Parse(node.Attributes.GetNamedItem("level").Value) > startNodeDepth || UmbracoSettings.UseDomainPrefixes || forceDomain))
|
||||
{
|
||||
if (node.ParentNode.Name != "root")
|
||||
{
|
||||
parentUrl = niceUrlJuno(int.Parse(node.ParentNode.Attributes.GetNamedItem("id").Value), startNodeDepth, currentDomain, forceDomain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only return the current node url if we're at the startnodedepth or higher
|
||||
if (int.Parse(node.Attributes.GetNamedItem("level").Value) >= startNodeDepth)
|
||||
return parentUrl + "/" + node.Attributes.GetNamedItem("urlName").Value;
|
||||
else if (node.PreviousSibling != null)
|
||||
return "/" + node.Attributes.GetNamedItem("urlName").Value;
|
||||
else
|
||||
return "/";
|
||||
}
|
||||
|
||||
private static string GetDomainIfExists(string currentDomain, int nodeId)
|
||||
{
|
||||
Domain[] domains = Domain.GetDomainsById(nodeId);
|
||||
if (domains.Length > 0)
|
||||
{
|
||||
if (currentDomain != String.Empty)
|
||||
{
|
||||
foreach (Domain d in domains)
|
||||
{
|
||||
// if there's multiple domains we'll prefer to use the same domain as the current request
|
||||
if (currentDomain == d.Name.ToLower() || d.Name.EndsWith(currentDomain, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
if (d.Name.StartsWith("http", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
return d.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Format("{0}://{1}", UmbracoContext.Current.Request.Url.Scheme, d.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (domains[0].Name.StartsWith("http", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
return domains[0].Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Format("{0}://{1}", UmbracoContext.Current.Request.Url.Scheme, domains[0].Name);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static string NiceUrlFetch(int nodeID, int startNodeDepth, bool forceDomain)
|
||||
{
|
||||
bool directoryUrls = GlobalSettings.UseDirectoryUrls;
|
||||
string baseUrl = SystemDirectories.Root; // SystemDirectories.Umbraco;
|
||||
string junoUrl = niceUrlJuno(nodeID, startNodeDepth, HttpContext.Current.Request.ServerVariables["SERVER_NAME"].ToLower(), forceDomain);
|
||||
return appendUrlExtension(baseUrl, directoryUrls, junoUrl);
|
||||
|
||||
}
|
||||
|
||||
private static string appendUrlExtension(string baseUrl, bool directoryUrls, string tempUrl)
|
||||
{
|
||||
if (!directoryUrls)
|
||||
{
|
||||
// append .aspx extension if the url includes other than just the domain name
|
||||
if (!String.IsNullOrEmpty(tempUrl) && tempUrl != "/" &&
|
||||
(!tempUrl.StartsWith("http://") || tempUrl.LastIndexOf("/") > 7))
|
||||
tempUrl = baseUrl + tempUrl + ".aspx";
|
||||
}
|
||||
else
|
||||
{
|
||||
tempUrl = baseUrl + tempUrl;
|
||||
if (tempUrl != "/" && UmbracoSettings.AddTrailingSlash)
|
||||
tempUrl += "/";
|
||||
}
|
||||
return tempUrl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string with the data from the given element of a node. Both elements (data-fields)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2010
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F0242771-6DE6-4E03-BD3A-7B79BA79105B}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
|
||||
Reference in New Issue
Block a user