refactor routing

This commit is contained in:
sgay
2012-07-20 18:54:59 -02:00
parent 25b06a88ab
commit 5fbf4379cc
16 changed files with 368 additions and 403 deletions

View File

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

View File

@@ -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
}
}

View File

@@ -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

View 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('/');
}
}
}

View File

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

View File

@@ -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;

View File

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

View File

@@ -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)

View File

@@ -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)
{

View File

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

View File

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

View File

@@ -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(

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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)

View File

@@ -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