Merge
This commit is contained in:
@@ -25,7 +25,7 @@ namespace Umbraco.Tests.Routing
|
||||
|
||||
void InitializeLanguagesAndDomains()
|
||||
{
|
||||
var domains = Domain.GetDomains();
|
||||
var domains = Domain.GetDomains(true); // we want wildcards too here
|
||||
foreach (var d in domains)
|
||||
d.Delete();
|
||||
|
||||
|
||||
@@ -26,6 +26,10 @@ namespace Umbraco.Tests.Routing
|
||||
true);
|
||||
|
||||
SettingsForTests.SettingsFilePath = Core.IO.IOHelper.MapPath(Core.IO.SystemDirectories.Config + Path.DirectorySeparatorChar, false);
|
||||
|
||||
SiteDomainHelperResolver.Reset();
|
||||
SiteDomainHelperResolver.Current = new SiteDomainHelperResolver(new SiteDomainHelper());
|
||||
FreezeResolution();
|
||||
}
|
||||
|
||||
internal override IRoutesCache GetRoutesCache()
|
||||
@@ -58,14 +62,14 @@ namespace Umbraco.Tests.Routing
|
||||
|
||||
foreach (var sample in samples)
|
||||
{
|
||||
var result = routingContext.NiceUrlProvider.GetNiceUrl(sample.Key);
|
||||
var result = routingContext.UrlProvider.GetUrl(sample.Key);
|
||||
Assert.AreEqual(sample.Value, result);
|
||||
}
|
||||
|
||||
var randomSample = new KeyValuePair<int, string>(1177, "/home/sub1/custom-sub-1");
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
var result = routingContext.NiceUrlProvider.GetNiceUrl(randomSample.Key);
|
||||
var result = routingContext.UrlProvider.GetUrl(randomSample.Key);
|
||||
Assert.AreEqual(randomSample.Value, result);
|
||||
}
|
||||
|
||||
@@ -106,7 +110,7 @@ namespace Umbraco.Tests.Routing
|
||||
SettingsForTests.HideTopLevelNodeFromPath = false;
|
||||
SettingsForTests.UseDomainPrefixes = false;
|
||||
|
||||
var result = routingContext.NiceUrlProvider.GetNiceUrl(nodeId);
|
||||
var result = routingContext.UrlProvider.GetUrl(nodeId);
|
||||
Assert.AreEqual(niceUrlMatch, result);
|
||||
}
|
||||
|
||||
@@ -129,7 +133,7 @@ namespace Umbraco.Tests.Routing
|
||||
SettingsForTests.HideTopLevelNodeFromPath = true;
|
||||
SettingsForTests.UseDomainPrefixes = false;
|
||||
|
||||
var result = routingContext.NiceUrlProvider.GetNiceUrl(nodeId);
|
||||
var result = routingContext.UrlProvider.GetUrl(nodeId);
|
||||
Assert.AreEqual(niceUrlMatch, result);
|
||||
}
|
||||
|
||||
@@ -142,14 +146,14 @@ namespace Umbraco.Tests.Routing
|
||||
SettingsForTests.HideTopLevelNodeFromPath = false;
|
||||
|
||||
SettingsForTests.UseDomainPrefixes = false;
|
||||
Assert.AreEqual("/home/sub1/custom-sub-1/", routingContext.NiceUrlProvider.GetNiceUrl(1177));
|
||||
Assert.AreEqual("/home/sub1/custom-sub-1/", routingContext.UrlProvider.GetUrl(1177));
|
||||
|
||||
SettingsForTests.UseDomainPrefixes = true;
|
||||
Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", routingContext.NiceUrlProvider.GetNiceUrl(1177));
|
||||
Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", routingContext.UrlProvider.GetUrl(1177));
|
||||
|
||||
SettingsForTests.UseDomainPrefixes = false;
|
||||
routingContext.NiceUrlProvider.EnforceAbsoluteUrls = true;
|
||||
Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", routingContext.NiceUrlProvider.GetNiceUrl(1177));
|
||||
routingContext.UrlProvider.EnforceAbsoluteUrls = true;
|
||||
Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", routingContext.UrlProvider.GetUrl(1177));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -161,12 +165,12 @@ namespace Umbraco.Tests.Routing
|
||||
SettingsForTests.HideTopLevelNodeFromPath = false;
|
||||
|
||||
SettingsForTests.UseDomainPrefixes = false;
|
||||
Assert.AreEqual("#", routingContext.NiceUrlProvider.GetNiceUrl(999999));
|
||||
Assert.AreEqual("#", routingContext.UrlProvider.GetUrl(999999));
|
||||
SettingsForTests.UseDomainPrefixes = true;
|
||||
Assert.AreEqual("#", routingContext.NiceUrlProvider.GetNiceUrl(999999));
|
||||
Assert.AreEqual("#", routingContext.UrlProvider.GetUrl(999999));
|
||||
SettingsForTests.UseDomainPrefixes = false;
|
||||
routingContext.NiceUrlProvider.EnforceAbsoluteUrls = true;
|
||||
Assert.AreEqual("#", routingContext.NiceUrlProvider.GetNiceUrl(999999));
|
||||
routingContext.UrlProvider.EnforceAbsoluteUrls = true;
|
||||
Assert.AreEqual("#", routingContext.UrlProvider.GetUrl(999999));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,10 @@ namespace Umbraco.Tests.Routing
|
||||
|
||||
// ensure we can create them although the content is not in the database
|
||||
TestHelper.DropForeignKeys("umbracoDomains");
|
||||
|
||||
SiteDomainHelperResolver.Reset();
|
||||
SiteDomainHelperResolver.Current = new SiteDomainHelperResolver(new SiteDomainHelper());
|
||||
FreezeResolution();
|
||||
}
|
||||
|
||||
internal override IRoutesCache GetRoutesCache()
|
||||
@@ -202,7 +206,7 @@ namespace Umbraco.Tests.Routing
|
||||
SetDomains1();
|
||||
|
||||
var currentUri = new Uri(currentUrl);
|
||||
var result = routingContext.NiceUrlProvider.GetNiceUrl(nodeId, currentUri, absolute);
|
||||
var result = routingContext.UrlProvider.GetUrl(nodeId, currentUri, absolute);
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
@@ -231,7 +235,7 @@ namespace Umbraco.Tests.Routing
|
||||
SetDomains2();
|
||||
|
||||
var currentUri = new Uri(currentUrl);
|
||||
var result = routingContext.NiceUrlProvider.GetNiceUrl(nodeId, currentUri, absolute);
|
||||
var result = routingContext.UrlProvider.GetUrl(nodeId, currentUri, absolute);
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
@@ -252,7 +256,7 @@ namespace Umbraco.Tests.Routing
|
||||
SetDomains3();
|
||||
|
||||
var currentUri = new Uri(currentUrl);
|
||||
var result = routingContext.NiceUrlProvider.GetNiceUrl(nodeId, currentUri, absolute);
|
||||
var result = routingContext.UrlProvider.GetUrl(nodeId, currentUri, absolute);
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
@@ -279,7 +283,7 @@ namespace Umbraco.Tests.Routing
|
||||
SetDomains4();
|
||||
|
||||
var currentUri = new Uri(currentUrl);
|
||||
var result = routingContext.NiceUrlProvider.GetNiceUrl(nodeId, currentUri, absolute);
|
||||
var result = routingContext.UrlProvider.GetUrl(nodeId, currentUri, absolute);
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
@@ -296,17 +300,17 @@ namespace Umbraco.Tests.Routing
|
||||
SetDomains4();
|
||||
|
||||
string ignore;
|
||||
ignore = routingContext.NiceUrlProvider.GetNiceUrl(1001, new Uri("http://domain1.com"), false);
|
||||
ignore = routingContext.NiceUrlProvider.GetNiceUrl(10011, new Uri("http://domain1.com"), false);
|
||||
ignore = routingContext.NiceUrlProvider.GetNiceUrl(100111, new Uri("http://domain1.com"), false);
|
||||
ignore = routingContext.NiceUrlProvider.GetNiceUrl(10012, new Uri("http://domain1.com"), false);
|
||||
ignore = routingContext.NiceUrlProvider.GetNiceUrl(100121, new Uri("http://domain1.com"), false);
|
||||
ignore = routingContext.NiceUrlProvider.GetNiceUrl(10013, new Uri("http://domain1.com"), false);
|
||||
ignore = routingContext.NiceUrlProvider.GetNiceUrl(1002, new Uri("http://domain1.com"), false);
|
||||
ignore = routingContext.NiceUrlProvider.GetNiceUrl(1001, new Uri("http://domain2.com"), false);
|
||||
ignore = routingContext.NiceUrlProvider.GetNiceUrl(10011, new Uri("http://domain2.com"), false);
|
||||
ignore = routingContext.NiceUrlProvider.GetNiceUrl(100111, new Uri("http://domain2.com"), false);
|
||||
ignore = routingContext.NiceUrlProvider.GetNiceUrl(1002, new Uri("http://domain2.com"), false);
|
||||
ignore = routingContext.UrlProvider.GetUrl(1001, new Uri("http://domain1.com"), false);
|
||||
ignore = routingContext.UrlProvider.GetUrl(10011, new Uri("http://domain1.com"), false);
|
||||
ignore = routingContext.UrlProvider.GetUrl(100111, new Uri("http://domain1.com"), false);
|
||||
ignore = routingContext.UrlProvider.GetUrl(10012, new Uri("http://domain1.com"), false);
|
||||
ignore = routingContext.UrlProvider.GetUrl(100121, new Uri("http://domain1.com"), false);
|
||||
ignore = routingContext.UrlProvider.GetUrl(10013, new Uri("http://domain1.com"), false);
|
||||
ignore = routingContext.UrlProvider.GetUrl(1002, new Uri("http://domain1.com"), false);
|
||||
ignore = routingContext.UrlProvider.GetUrl(1001, new Uri("http://domain2.com"), false);
|
||||
ignore = routingContext.UrlProvider.GetUrl(10011, new Uri("http://domain2.com"), false);
|
||||
ignore = routingContext.UrlProvider.GetUrl(100111, new Uri("http://domain2.com"), false);
|
||||
ignore = routingContext.UrlProvider.GetUrl(1002, new Uri("http://domain2.com"), false);
|
||||
|
||||
var cachedRoutes = ((DefaultRoutesCache)routingContext.RoutesCache).GetCachedRoutes();
|
||||
Assert.AreEqual(7, cachedRoutes.Count);
|
||||
@@ -323,15 +327,15 @@ namespace Umbraco.Tests.Routing
|
||||
CheckRoute(cachedRoutes, cachedIds, 1002, "/1002");
|
||||
|
||||
// use the cache
|
||||
Assert.AreEqual("/", routingContext.NiceUrlProvider.GetNiceUrl(1001, new Uri("http://domain1.com"), false));
|
||||
Assert.AreEqual("/en/", routingContext.NiceUrlProvider.GetNiceUrl(10011, new Uri("http://domain1.com"), false));
|
||||
Assert.AreEqual("/en/1001-1-1/", routingContext.NiceUrlProvider.GetNiceUrl(100111, new Uri("http://domain1.com"), false));
|
||||
Assert.AreEqual("/fr/", routingContext.NiceUrlProvider.GetNiceUrl(10012, new Uri("http://domain1.com"), false));
|
||||
Assert.AreEqual("/fr/1001-2-1/", routingContext.NiceUrlProvider.GetNiceUrl(100121, new Uri("http://domain1.com"), false));
|
||||
Assert.AreEqual("/1001-3/", routingContext.NiceUrlProvider.GetNiceUrl(10013, new Uri("http://domain1.com"), false));
|
||||
Assert.AreEqual("/1002/", routingContext.NiceUrlProvider.GetNiceUrl(1002, new Uri("http://domain1.com"), false));
|
||||
Assert.AreEqual("/", routingContext.UrlProvider.GetUrl(1001, new Uri("http://domain1.com"), false));
|
||||
Assert.AreEqual("/en/", routingContext.UrlProvider.GetUrl(10011, new Uri("http://domain1.com"), false));
|
||||
Assert.AreEqual("/en/1001-1-1/", routingContext.UrlProvider.GetUrl(100111, new Uri("http://domain1.com"), false));
|
||||
Assert.AreEqual("/fr/", routingContext.UrlProvider.GetUrl(10012, new Uri("http://domain1.com"), false));
|
||||
Assert.AreEqual("/fr/1001-2-1/", routingContext.UrlProvider.GetUrl(100121, new Uri("http://domain1.com"), false));
|
||||
Assert.AreEqual("/1001-3/", routingContext.UrlProvider.GetUrl(10013, new Uri("http://domain1.com"), false));
|
||||
Assert.AreEqual("/1002/", routingContext.UrlProvider.GetUrl(1002, new Uri("http://domain1.com"), false));
|
||||
|
||||
Assert.AreEqual("http://domain1.com/fr/1001-2-1/", routingContext.NiceUrlProvider.GetNiceUrl(100121, new Uri("http://domain2.com"), false));
|
||||
Assert.AreEqual("http://domain1.com/fr/1001-2-1/", routingContext.UrlProvider.GetUrl(100121, new Uri("http://domain2.com"), false));
|
||||
}
|
||||
|
||||
void CheckRoute(IDictionary<int, string> routes, IDictionary<string, int> ids, int id, string route)
|
||||
@@ -354,23 +358,23 @@ namespace Umbraco.Tests.Routing
|
||||
SetDomains4();
|
||||
|
||||
SettingsForTests.UseDomainPrefixes = false;
|
||||
Assert.AreEqual("/en/1001-1-1/", routingContext.NiceUrlProvider.GetNiceUrl(100111));
|
||||
Assert.AreEqual("http://domain3.com/en/1003-1-1/", routingContext.NiceUrlProvider.GetNiceUrl(100311));
|
||||
Assert.AreEqual("/en/1001-1-1/", routingContext.UrlProvider.GetUrl(100111));
|
||||
Assert.AreEqual("http://domain3.com/en/1003-1-1/", routingContext.UrlProvider.GetUrl(100311));
|
||||
|
||||
SettingsForTests.UseDomainPrefixes = true;
|
||||
Assert.AreEqual("http://domain1.com/en/1001-1-1/", routingContext.NiceUrlProvider.GetNiceUrl(100111));
|
||||
Assert.AreEqual("http://domain3.com/en/1003-1-1/", routingContext.NiceUrlProvider.GetNiceUrl(100311));
|
||||
Assert.AreEqual("http://domain1.com/en/1001-1-1/", routingContext.UrlProvider.GetUrl(100111));
|
||||
Assert.AreEqual("http://domain3.com/en/1003-1-1/", routingContext.UrlProvider.GetUrl(100311));
|
||||
|
||||
SettingsForTests.UseDomainPrefixes = false;
|
||||
routingContext.NiceUrlProvider.EnforceAbsoluteUrls = true;
|
||||
Assert.AreEqual("http://domain1.com/en/1001-1-1/", routingContext.NiceUrlProvider.GetNiceUrl(100111));
|
||||
Assert.AreEqual("http://domain3.com/en/1003-1-1/", routingContext.NiceUrlProvider.GetNiceUrl(100311));
|
||||
routingContext.UrlProvider.EnforceAbsoluteUrls = true;
|
||||
Assert.AreEqual("http://domain1.com/en/1001-1-1/", routingContext.UrlProvider.GetUrl(100111));
|
||||
Assert.AreEqual("http://domain3.com/en/1003-1-1/", routingContext.UrlProvider.GetUrl(100311));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Get_Nice_Url_Alternate()
|
||||
{
|
||||
var routingContext = GetRoutingContext("http://domain1.com/test", 1111);
|
||||
var routingContext = GetRoutingContext("http://domain1.com/en/test", 1111);
|
||||
|
||||
SettingsForTests.UseDirectoryUrls = true;
|
||||
SettingsForTests.HideTopLevelNodeFromPath = false;
|
||||
@@ -378,13 +382,12 @@ namespace Umbraco.Tests.Routing
|
||||
InitializeLanguagesAndDomains();
|
||||
SetDomains5();
|
||||
|
||||
var result = routingContext.NiceUrlProvider.GetAllAbsoluteNiceUrls(100111);
|
||||
var url = routingContext.UrlProvider.GetUrl(100111, true);
|
||||
Assert.AreEqual("http://domain1.com/en/1001-1-1/", url);
|
||||
|
||||
var result = routingContext.UrlProvider.GetOtherUrls(100111).ToArray();
|
||||
|
||||
// will always get absolute urls
|
||||
// all of them
|
||||
// including the local one - duplicate?! - then must manually exclude?
|
||||
Assert.AreEqual(3, result.Count());
|
||||
Assert.IsTrue(result.Contains("http://domain1.com/en/1001-1-1/"));
|
||||
Assert.AreEqual(2, result.Count());
|
||||
Assert.IsTrue(result.Contains("http://domain1a.com/en/1001-1-1/"));
|
||||
Assert.IsTrue(result.Contains("http://domain1b.com/en/1001-1-1/"));
|
||||
}
|
||||
|
||||
309
src/Umbraco.Tests/Routing/SiteDomainHelperTests.cs
Normal file
309
src/Umbraco.Tests/Routing/SiteDomainHelperTests.cs
Normal file
@@ -0,0 +1,309 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Web.Routing;
|
||||
using umbraco.cms.businesslogic.web;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Umbraco.Tests.Routing
|
||||
{
|
||||
[TestFixture]
|
||||
public class SiteDomainHelperTests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
SiteDomainHelper.Clear(); // assuming this works!
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddSites()
|
||||
{
|
||||
SiteDomainHelper.AddSite("site1", "domain1.com", "domain1.net", "domain1.org");
|
||||
SiteDomainHelper.AddSite("site2", "domain2.com", "domain2.net", "domain2.org");
|
||||
|
||||
var sites = SiteDomainHelper.Sites;
|
||||
|
||||
Assert.AreEqual(2, sites.Count);
|
||||
|
||||
Assert.Contains("site1", sites.Keys);
|
||||
Assert.Contains("site2", sites.Keys);
|
||||
|
||||
var domains = sites["site1"];
|
||||
Assert.AreEqual(3, domains.Count());
|
||||
Assert.Contains("domain1.com", domains);
|
||||
Assert.Contains("domain1.net", domains);
|
||||
Assert.Contains("domain1.org", domains);
|
||||
|
||||
domains = sites["site2"];
|
||||
Assert.AreEqual(3, domains.Count());
|
||||
Assert.Contains("domain2.com", domains);
|
||||
Assert.Contains("domain2.net", domains);
|
||||
Assert.Contains("domain2.org", domains);
|
||||
}
|
||||
|
||||
[TestCase("foo")] // that one is suspect
|
||||
[TestCase("domain.com")]
|
||||
[TestCase("domain.com/")]
|
||||
[TestCase("domain.com:12")]
|
||||
[TestCase("domain.com:12/")]
|
||||
[TestCase("http://www.domain.com")]
|
||||
[TestCase("http://www.domain.com:12")]
|
||||
[TestCase("http://www.domain.com:12/")]
|
||||
[TestCase("https://foo.www.domain.com")]
|
||||
[TestCase("https://foo.www.domain.com:5478/")]
|
||||
public void AddValidSite(string domain)
|
||||
{
|
||||
SiteDomainHelper.AddSite("site1", domain);
|
||||
}
|
||||
|
||||
[TestCase("domain.com/foo")]
|
||||
[TestCase("http:/domain.com")]
|
||||
[TestCase("*")]
|
||||
public void AddInvalidSite(string domain)
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => SiteDomainHelper.AddSite("site1", domain));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddRemoveSites()
|
||||
{
|
||||
SiteDomainHelper.AddSite("site1", "domain1.com", "domain1.net", "domain1.org");
|
||||
SiteDomainHelper.AddSite("site2", "domain2.com", "domain2.net", "domain2.org");
|
||||
|
||||
var sites = SiteDomainHelper.Sites;
|
||||
|
||||
SiteDomainHelper.RemoveSite("site1");
|
||||
SiteDomainHelper.RemoveSite("site3");
|
||||
|
||||
Assert.AreEqual(1, sites.Count);
|
||||
|
||||
Assert.Contains("site2", sites.Keys);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddSiteAgain()
|
||||
{
|
||||
SiteDomainHelper.AddSite("site1", "domain1.com", "domain1.net", "domain1.org");
|
||||
SiteDomainHelper.AddSite("site1", "domain2.com", "domain1.net");
|
||||
|
||||
var sites = SiteDomainHelper.Sites;
|
||||
|
||||
Assert.AreEqual(1, sites.Count);
|
||||
|
||||
Assert.Contains("site1", sites.Keys);
|
||||
|
||||
var domains = sites["site1"];
|
||||
Assert.AreEqual(2, domains.Count());
|
||||
Assert.Contains("domain2.com", domains);
|
||||
Assert.Contains("domain1.net", domains);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BindSitesOnce()
|
||||
{
|
||||
SiteDomainHelper.AddSite("site1", "domain1.com", "domain1.net", "domain1.org");
|
||||
SiteDomainHelper.AddSite("site2", "domain2.com", "domain2.net", "domain2.org");
|
||||
SiteDomainHelper.AddSite("site3", "domain3.com", "domain3.net", "domain3.org");
|
||||
SiteDomainHelper.AddSite("site4", "domain4.com", "domain4.net", "domain4.org");
|
||||
|
||||
SiteDomainHelper.BindSites("site1", "site2");
|
||||
|
||||
var bindings = SiteDomainHelper.Bindings;
|
||||
|
||||
Assert.AreEqual(2, bindings.Count);
|
||||
Assert.Contains("site1", bindings.Keys);
|
||||
Assert.Contains("site2", bindings.Keys);
|
||||
|
||||
var others = bindings["site1"];
|
||||
Assert.AreEqual(1, others.Count);
|
||||
Assert.Contains("site2", others);
|
||||
|
||||
others = bindings["site2"];
|
||||
Assert.AreEqual(1, others.Count);
|
||||
Assert.Contains("site1", others);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BindMoreSites()
|
||||
{
|
||||
SiteDomainHelper.AddSite("site1", "domain1.com", "domain1.net", "domain1.org");
|
||||
SiteDomainHelper.AddSite("site2", "domain2.com", "domain2.net", "domain2.org");
|
||||
SiteDomainHelper.AddSite("site3", "domain3.com", "domain3.net", "domain3.org");
|
||||
SiteDomainHelper.AddSite("site4", "domain4.com", "domain4.net", "domain4.org");
|
||||
|
||||
SiteDomainHelper.BindSites("site1", "site2");
|
||||
SiteDomainHelper.BindSites("site1", "site3");
|
||||
|
||||
var bindings = SiteDomainHelper.Bindings;
|
||||
|
||||
Assert.AreEqual(3, bindings.Count);
|
||||
Assert.Contains("site1", bindings.Keys);
|
||||
Assert.Contains("site2", bindings.Keys);
|
||||
Assert.Contains("site3", bindings.Keys);
|
||||
|
||||
var others = bindings["site1"];
|
||||
Assert.AreEqual(2, others.Count);
|
||||
Assert.Contains("site2", others);
|
||||
Assert.Contains("site3", others);
|
||||
|
||||
others = bindings["site2"];
|
||||
Assert.AreEqual(2, others.Count);
|
||||
Assert.Contains("site1", others);
|
||||
Assert.Contains("site3", others);
|
||||
|
||||
others = bindings["site3"];
|
||||
Assert.AreEqual(2, others.Count);
|
||||
Assert.Contains("site1", others);
|
||||
Assert.Contains("site2", others);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MapDomain()
|
||||
{
|
||||
SiteDomainHelper.AddSite("site1", "domain1.com", "domain1.net", "domain1.org");
|
||||
SiteDomainHelper.AddSite("site2", "domain2.com", "domain2.net", "domain2.org");
|
||||
SiteDomainHelper.AddSite("site3", "domain3.com", "domain3.net", "domain3.org");
|
||||
SiteDomainHelper.AddSite("site4", "domain4.com", "domain4.net", "domain4.org");
|
||||
|
||||
//SiteDomainHelper.BindSites("site1", "site3");
|
||||
//SiteDomainHelper.BindSites("site2", "site4");
|
||||
|
||||
// map methods are not static because we can override them
|
||||
var helper = new SiteDomainHelper();
|
||||
|
||||
// current is a site1 uri, domains contain current
|
||||
// so we'll get current
|
||||
//
|
||||
var current = new Uri("http://domain1.com/foo/bar");
|
||||
var output = helper.MapDomain(current, new[]
|
||||
{
|
||||
new DomainAndUri(new MockDomain("domain1.com"), Uri.UriSchemeHttp),
|
||||
new DomainAndUri(new MockDomain("domain2.com"), Uri.UriSchemeHttp),
|
||||
}).Uri.ToString();
|
||||
Assert.AreEqual("http://domain1.com/", output);
|
||||
|
||||
// current is a site1 uri, domains do not contain current
|
||||
// so we'll get the corresponding site1 domain
|
||||
//
|
||||
current = new Uri("http://domain1.com/foo/bar");
|
||||
output = helper.MapDomain(current, new[]
|
||||
{
|
||||
new DomainAndUri(new MockDomain("domain1.net"), Uri.UriSchemeHttp),
|
||||
new DomainAndUri(new MockDomain("domain2.net"), Uri.UriSchemeHttp)
|
||||
}).Uri.ToString();
|
||||
Assert.AreEqual("http://domain1.net/", output);
|
||||
|
||||
// current is a site1 uri, domains do not contain current
|
||||
// so we'll get the corresponding site1 domain
|
||||
// order does not matter
|
||||
//
|
||||
current = new Uri("http://domain1.com/foo/bar");
|
||||
output = helper.MapDomain(current, new[]
|
||||
{
|
||||
new DomainAndUri(new MockDomain("domain2.net"), Uri.UriSchemeHttp),
|
||||
new DomainAndUri(new MockDomain("domain1.net"), Uri.UriSchemeHttp)
|
||||
}).Uri.ToString();
|
||||
Assert.AreEqual("http://domain1.net/", output);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MapDomains()
|
||||
{
|
||||
SiteDomainHelper.AddSite("site1", "domain1.com", "domain1.net", "domain1.org");
|
||||
SiteDomainHelper.AddSite("site2", "domain2.com", "domain2.net", "domain2.org");
|
||||
SiteDomainHelper.AddSite("site3", "domain3.com", "domain3.net", "domain3.org");
|
||||
SiteDomainHelper.AddSite("site4", "domain4.com", "domain4.net", "domain4.org");
|
||||
|
||||
// map methods are not static because we can override them
|
||||
var helper = new SiteDomainHelper();
|
||||
|
||||
// the rule is:
|
||||
// - exclude the current domain
|
||||
// - exclude what MapDomain would return
|
||||
// - return all domains from same site, or bound sites
|
||||
|
||||
// current is a site1 uri, domains contains current
|
||||
//
|
||||
var current = new Uri("http://domain1.com/foo/bar");
|
||||
var output = helper.MapDomains(current, new[]
|
||||
{
|
||||
new DomainAndUri(new MockDomain("domain1.com"), Uri.UriSchemeHttp), // no: current + what MapDomain would pick
|
||||
new DomainAndUri(new MockDomain("domain2.com"), Uri.UriSchemeHttp), // no: not same site
|
||||
new DomainAndUri(new MockDomain("domain3.com"), Uri.UriSchemeHttp), // no: not same site
|
||||
new DomainAndUri(new MockDomain("domain4.com"), Uri.UriSchemeHttp), // no: not same site
|
||||
new DomainAndUri(new MockDomain("domain1.org"), Uri.UriSchemeHttp), // yes: same site (though bogus setup)
|
||||
}, true).ToArray();
|
||||
|
||||
Assert.AreEqual(1, output.Count());
|
||||
Assert.Contains("http://domain1.org/", output.Select(d => d.Uri.ToString()).ToArray());
|
||||
|
||||
// current is a site1 uri, domains does not contain current
|
||||
//
|
||||
current = new Uri("http://domain1.com/foo/bar");
|
||||
output = helper.MapDomains(current, new[]
|
||||
{
|
||||
new DomainAndUri(new MockDomain("domain1.net"), Uri.UriSchemeHttp), // no: what MapDomain would pick
|
||||
new DomainAndUri(new MockDomain("domain2.com"), Uri.UriSchemeHttp), // no: not same site
|
||||
new DomainAndUri(new MockDomain("domain3.com"), Uri.UriSchemeHttp), // no: not same site
|
||||
new DomainAndUri(new MockDomain("domain4.com"), Uri.UriSchemeHttp), // no: not same site
|
||||
new DomainAndUri(new MockDomain("domain1.org"), Uri.UriSchemeHttp), // yes: same site (though bogus setup)
|
||||
}, true).ToArray();
|
||||
|
||||
Assert.AreEqual(1, output.Count());
|
||||
Assert.Contains("http://domain1.org/", output.Select(d => d.Uri.ToString()).ToArray());
|
||||
|
||||
SiteDomainHelper.BindSites("site1", "site3");
|
||||
SiteDomainHelper.BindSites("site2", "site4");
|
||||
|
||||
// current is a site1 uri, domains contains current
|
||||
//
|
||||
current = new Uri("http://domain1.com/foo/bar");
|
||||
output = helper.MapDomains(current, new[]
|
||||
{
|
||||
new DomainAndUri(new MockDomain("domain1.com"), Uri.UriSchemeHttp), // no: current + what MapDomain would pick
|
||||
new DomainAndUri(new MockDomain("domain2.com"), Uri.UriSchemeHttp), // no: not same site
|
||||
new DomainAndUri(new MockDomain("domain3.com"), Uri.UriSchemeHttp), // yes: bound site
|
||||
new DomainAndUri(new MockDomain("domain3.org"), Uri.UriSchemeHttp), // yes: bound site
|
||||
new DomainAndUri(new MockDomain("domain4.com"), Uri.UriSchemeHttp), // no: not same site
|
||||
new DomainAndUri(new MockDomain("domain1.org"), Uri.UriSchemeHttp), // yes: same site (though bogus setup)
|
||||
}, true).ToArray();
|
||||
|
||||
Assert.AreEqual(3, output.Count());
|
||||
Assert.Contains("http://domain1.org/", output.Select(d => d.Uri.ToString()).ToArray());
|
||||
Assert.Contains("http://domain3.com/", output.Select(d => d.Uri.ToString()).ToArray());
|
||||
Assert.Contains("http://domain3.org/", output.Select(d => d.Uri.ToString()).ToArray());
|
||||
|
||||
// current is a site1 uri, domains does not contain current
|
||||
//
|
||||
current = new Uri("http://domain1.com/foo/bar");
|
||||
output = helper.MapDomains(current, new[]
|
||||
{
|
||||
new DomainAndUri(new MockDomain("domain1.net"), Uri.UriSchemeHttp), // no: what MapDomain would pick
|
||||
new DomainAndUri(new MockDomain("domain2.com"), Uri.UriSchemeHttp), // no: not same site
|
||||
new DomainAndUri(new MockDomain("domain3.com"), Uri.UriSchemeHttp), // yes: bound site
|
||||
new DomainAndUri(new MockDomain("domain3.org"), Uri.UriSchemeHttp), // yes: bound site
|
||||
new DomainAndUri(new MockDomain("domain4.com"), Uri.UriSchemeHttp), // no: not same site
|
||||
new DomainAndUri(new MockDomain("domain1.org"), Uri.UriSchemeHttp), // yes: same site (though bogus setup)
|
||||
}, true).ToArray();
|
||||
|
||||
Assert.AreEqual(3, output.Count());
|
||||
Assert.Contains("http://domain1.org/", output.Select(d => d.Uri.ToString()).ToArray());
|
||||
Assert.Contains("http://domain3.com/", output.Select(d => d.Uri.ToString()).ToArray());
|
||||
Assert.Contains("http://domain3.org/", output.Select(d => d.Uri.ToString()).ToArray());
|
||||
}
|
||||
|
||||
class MockDomain : Domain
|
||||
{
|
||||
private static readonly FieldInfo NameField = typeof (Domain).GetField("_name", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
public MockDomain(string name)
|
||||
{
|
||||
NameField.SetValue(this, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ namespace Umbraco.Tests.Routing
|
||||
|
||||
// get the nice url for 100111
|
||||
routingContext = GetRoutingContext(url);
|
||||
Assert.AreEqual("http://domain2.com/1001-1-1/", routingContext.NiceUrlProvider.GetNiceUrl(100111, true));
|
||||
Assert.AreEqual("http://domain2.com/1001-1-1/", routingContext.UrlProvider.GetUrl(100111, true));
|
||||
|
||||
// check that the proper route has been cached
|
||||
var cachedRoutes = ((DefaultRoutesCache)routingContext.RoutesCache).GetCachedRoutes();
|
||||
@@ -59,7 +59,7 @@ namespace Umbraco.Tests.Routing
|
||||
//Assert.AreEqual("1001/1001-1/1001-1-1", cachedRoutes[100111]); // yes
|
||||
|
||||
// what's the nice url now?
|
||||
Assert.AreEqual("http://domain2.com/1001-1-1/", routingContext.NiceUrlProvider.GetNiceUrl(100111)); // good
|
||||
Assert.AreEqual("http://domain2.com/1001-1-1/", routingContext.UrlProvider.GetUrl(100111)); // good
|
||||
//Assert.AreEqual("http://domain1.com/1001-1/1001-1-1", routingContext.NiceUrlProvider.GetNiceUrl(100111, true)); // bad
|
||||
}
|
||||
|
||||
@@ -69,6 +69,10 @@ namespace Umbraco.Tests.Routing
|
||||
|
||||
// ensure we can create them although the content is not in the database
|
||||
TestHelper.DropForeignKeys("umbracoDomains");
|
||||
|
||||
SiteDomainHelperResolver.Reset();
|
||||
SiteDomainHelperResolver.Current = new SiteDomainHelperResolver(new SiteDomainHelper());
|
||||
FreezeResolution();
|
||||
}
|
||||
|
||||
internal override IRoutesCache GetRoutesCache()
|
||||
|
||||
@@ -34,13 +34,13 @@ namespace Umbraco.Tests.Routing
|
||||
|
||||
var umbracoContext = GetUmbracoContext(url, t.Id);
|
||||
var contentStore = new DefaultPublishedContentStore();
|
||||
var niceUrls = new NiceUrlProvider(contentStore, umbracoContext);
|
||||
var urlProvider = new UrlProvider(umbracoContext, contentStore, new IUrlProvider[] { new DefaultUrlProvider() });
|
||||
var routingContext = new RoutingContext(
|
||||
umbracoContext,
|
||||
lookups,
|
||||
new FakeLastChanceFinder(),
|
||||
contentStore,
|
||||
niceUrls,
|
||||
urlProvider,
|
||||
GetRoutesCache());
|
||||
|
||||
//assign the routing context back to the umbraco context
|
||||
|
||||
@@ -28,13 +28,13 @@ namespace Umbraco.Tests.TestHelpers
|
||||
{
|
||||
var umbracoContext = GetUmbracoContext(url, templateId, routeData);
|
||||
var contentStore = new DefaultPublishedContentStore();
|
||||
var niceUrls = new NiceUrlProvider(contentStore, umbracoContext);
|
||||
var urlProvider = new UrlProvider(umbracoContext, contentStore, new IUrlProvider[] { new DefaultUrlProvider() });
|
||||
var routingContext = new RoutingContext(
|
||||
umbracoContext,
|
||||
Enumerable.Empty<IContentFinder>(),
|
||||
new FakeLastChanceFinder(),
|
||||
contentStore,
|
||||
niceUrls,
|
||||
urlProvider,
|
||||
GetRoutesCache());
|
||||
|
||||
//assign the routing context back to the umbraco context
|
||||
|
||||
@@ -287,6 +287,7 @@
|
||||
<Compile Include="Routing\ContentFinderByNiceUrlWithDomainsTests.cs" />
|
||||
<Compile Include="Routing\DomainsAndCulturesTests.cs" />
|
||||
<Compile Include="Routing\NiceUrlsProviderWithDomainsTests.cs" />
|
||||
<Compile Include="Routing\SiteDomainHelperTests.cs" />
|
||||
<Compile Include="Routing\uQueryGetNodeIdByUrlTests.cs" />
|
||||
<Compile Include="Routing\UrlsWithNestedDomains.cs" />
|
||||
<Compile Include="Services\BaseServiceTest.cs" />
|
||||
|
||||
@@ -571,6 +571,7 @@
|
||||
<Content Include="Umbraco\Developer\RelationTypes\RelationTypesWebService.asmx" />
|
||||
<Content Include="Umbraco\Developer\RelationTypes\TreeMenu\ActionDeleteRelationType.js" />
|
||||
<Content Include="Umbraco\Developer\RelationTypes\TreeMenu\ActionNewRelationType.js" />
|
||||
<Content Include="Umbraco\Dialogs\AssignDomain2.aspx" />
|
||||
<Content Include="Umbraco\Dialogs\EditMacro.aspx" />
|
||||
<Content Include="Umbraco\Images\delete.gif" />
|
||||
<Content Include="Umbraco\Images\delete.png" />
|
||||
@@ -610,6 +611,7 @@
|
||||
<Content Include="Umbraco\Webservices\Api\MemberService.asmx" />
|
||||
<Content Include="Umbraco\Webservices\Api\StylesheetService.asmx" />
|
||||
<Content Include="Umbraco\Webservices\Api\TemplateService.asmx" />
|
||||
<Content Include="Umbraco_Client\Application\JQuery\jquery.validate.min.js" />
|
||||
<Content Include="Umbraco_Client\CodeMirror\Js\Lib\codemirror.css" />
|
||||
<Content Include="Umbraco_Client\CodeMirror\Js\Lib\codemirror.js" />
|
||||
<Content Include="Umbraco_Client\CodeMirror\Js\Lib\Util\closetag.js" />
|
||||
@@ -773,6 +775,8 @@
|
||||
<Content Include="Umbraco_Client\ContextMenu\Js\jquery.contextMenu.js" />
|
||||
<Content Include="Umbraco_Client\Dashboards\ExamineManagement.css" />
|
||||
<Content Include="Umbraco_Client\Dashboards\ExamineManagement.js" />
|
||||
<Content Include="Umbraco_Client\Dialogs\AssignDomain2.js" />
|
||||
<Content Include="Umbraco_Client\Dialogs\AssignDomain2.css" />
|
||||
<Content Include="Umbraco_Client\Dialogs\CreateDialog.css" />
|
||||
<Content Include="Umbraco_Client\Dialogs\EditMacro.css" />
|
||||
<Content Include="Umbraco_Client\Dialogs\EditMacro.js" />
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<link>http://umbraco.org</link>
|
||||
</creator>
|
||||
<area alias="actions">
|
||||
<key alias="assignDomain">Manage hostnames</key>
|
||||
<key alias="assignDomain">Culture and Hostnames</key>
|
||||
<key alias="auditTrail">Audit Trail</key>
|
||||
<key alias="browse">Browse Node</key>
|
||||
<key alias="copy">Copy</key>
|
||||
@@ -36,19 +36,31 @@
|
||||
<key alias="update">Update</key>
|
||||
</area>
|
||||
<area alias="assignDomain">
|
||||
<key alias="permissionDenied">Permission denied.</key>
|
||||
<key alias="addNew">Add new Domain</key>
|
||||
<key alias="invalidDomain">Invalid hostname</key>
|
||||
<key alias="remove">remove</key>
|
||||
<key alias="invalidNode">Invalid node.</key>
|
||||
<key alias="invalidDomain">Invalid domain format.</key>
|
||||
<key alias="duplicateDomain">Domain has already been assigned.</key>
|
||||
<key alias="domain">Domain</key>
|
||||
<key alias="language">Language</key>
|
||||
<key alias="domainCreated">New domain '%0%' has been created</key>
|
||||
<key alias="domainDeleted">Domain '%0%' is deleted</key>
|
||||
<key alias="domainExists">Domain '%0%' has already been assigned</key>
|
||||
<key alias="domainHelp">
|
||||
<![CDATA[eg: example.com, www.example.com, example.com:8080,<br/>
|
||||
https://www.example.com/, example.com/en, etc. Use * to match<br/>
|
||||
any domain and just set the culture.]]>
|
||||
<![CDATA[Valid domain names are: "example.com", "www.example.com", "example.com:8080" or
|
||||
"https://www.example.com/".<br /><br />One-level paths in domains are supported, eg. "example.com/en". However, they
|
||||
they should be avoided. Better use the culture setting above.]]>
|
||||
</key>
|
||||
<key alias="domainUpdated">Domain '%0%' has been updated</key>
|
||||
<key alias="orEdit">Edit Current Domains</key>
|
||||
<key alias="inherit">Inherit</key>
|
||||
<key alias="setLanguage">Culture</key>
|
||||
<key alias="setLanguageHelp">
|
||||
<![CDATA[Set the culture for nodes below the current node,<br /> or inherit culture from parent nodes. Will also apply<br />
|
||||
to the current node, unless a domain below applies too.]]>
|
||||
</key>
|
||||
<key alias="setDomains">Domains</key>
|
||||
</area>
|
||||
<area alias="auditTrails">
|
||||
<key alias="atViewingFor">Viewing for</key>
|
||||
|
||||
77
src/Umbraco.Web.UI/umbraco/dialogs/AssignDomain2.aspx
Normal file
77
src/Umbraco.Web.UI/umbraco/dialogs/AssignDomain2.aspx
Normal file
@@ -0,0 +1,77 @@
|
||||
<%@ Page Language="c#" MasterPageFile="../masterpages/umbracoDialog.Master" Codebehind="AssignDomain2.aspx.cs" AutoEventWireup="True" Inherits="umbraco.dialogs.AssignDomain2" %>
|
||||
<%@ Import Namespace="Umbraco.Web" %>
|
||||
<%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %>
|
||||
<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %>
|
||||
|
||||
<asp:Content ContentPlaceHolderID="head" runat="server">
|
||||
<umb:JsInclude runat="server" FilePath="Dialogs/AssignDomain2.js" PathNameAlias="UmbracoClient" />
|
||||
<umb:JsInclude runat="server" FilePath="Application/JQuery/jquery.validate.min.js" PathNameAlias="UmbracoClient" />
|
||||
<umb:CssInclude runat="server" FilePath="Dialogs/AssignDomain2.css" PathNameAlias="UmbracoClient" />
|
||||
<script type="text/javascript">
|
||||
|
||||
(function ($) {
|
||||
$(document).ready(function () {
|
||||
var dialog = new Umbraco.Dialogs.AssignDomain2({
|
||||
nodeId: <%=GetNodeId()%>,
|
||||
restServiceLocation: '<%=GetRestServicePath() %>',
|
||||
invalidDomain: '<%=umbraco.ui.Text("assignDomain", "invalidDomain") %>',
|
||||
duplicateDomain: '<%=umbraco.ui.Text("assignDomain", "duplicateDomain") %>',
|
||||
<asp:Literal runat="server" ID="data" />
|
||||
});
|
||||
dialog.init();
|
||||
});
|
||||
})(jQuery);
|
||||
|
||||
</script>
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ContentPlaceHolderID="body" runat="server">
|
||||
|
||||
<cc1:Feedback ID="feedback" runat="server" />
|
||||
|
||||
<div id="komask"></div>
|
||||
<div>
|
||||
<cc1:Pane runat="server" ID="pane_language">
|
||||
<cc1:PropertyPanel runat="server" ID="prop_language">
|
||||
<select class="language" name="language" data-bind="options: languages, optionsText: 'Code', optionsValue: 'Id', value: language, optionsCaption: '<%=umbraco.ui.Text("assignDomain", "inherit") %>'"></select>
|
||||
<br /><small><%=umbraco.ui.Text("assignDomain", "setLanguageHelp") %></small>
|
||||
</cc1:PropertyPanel>
|
||||
</cc1:Pane>
|
||||
|
||||
<cc1:Pane runat="server" ID="pane_domains">
|
||||
<cc1:PropertyPanel runat="server">
|
||||
<table class="domains" data-bind="visible: domains().length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%=umbraco.ui.Text("assignDomain", "domain") %></th>
|
||||
<th><%=umbraco.ui.Text("assignDomain", "language") %></th>
|
||||
<th />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-bind="foreach: domains">
|
||||
<tr>
|
||||
<td valign="top"><input class="domain duplicate" data-bind="value: Name, uniqueName: true" /><input type="hidden" value="0" /></td>
|
||||
<td valign="top"><select class="language" data-bind="options: $parent.languages, optionsText: 'Code', optionsValue: 'Id', value: Lang, uniqueName: true"></select></td>
|
||||
<td valign="top"><a href="#" class="remove" data-bind="click: $parent.removeDomain"><%=umbraco.ui.Text("assignDomain", "remove") %></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="addDomain">
|
||||
<tr>
|
||||
<td valign="top"><button data-bind="click: addDomain"><%=umbraco.ui.Text("assignDomain", "addNew") %></button></td>
|
||||
<td class="help"><small><%=umbraco.ui.Text("assignDomain", "domainHelp") %></small></td>
|
||||
</tr>
|
||||
</table>
|
||||
</cc1:PropertyPanel>
|
||||
</cc1:Pane>
|
||||
|
||||
<p>
|
||||
<asp:PlaceHolder runat="server" ID="phSave">
|
||||
<button id="btnSave"><%=umbraco.ui.Text("buttons", "save") %></button>
|
||||
<em><%=umbraco.ui.Text("general", "or")%></em>
|
||||
</asp:PlaceHolder>
|
||||
<a href="#" style="color: #0000ff;" onclick="UmbClientMgr.closeModalWindow()"><%=umbraco.ui.Text("general", "cancel")%></a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</asp:Content>
|
||||
4
src/Umbraco.Web.UI/umbraco_client/Application/JQuery/jquery.validate.min.js
vendored
Normal file
4
src/Umbraco.Web.UI/umbraco_client/Application/JQuery/jquery.validate.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -257,7 +257,7 @@ Umbraco.Application.Actions = function () {
|
||||
/// <summary></summary>
|
||||
|
||||
if (UmbClientMgr.mainTree().getActionNode().nodeId != '-1' && UmbClientMgr.mainTree().getActionNode().nodeType != '') {
|
||||
UmbClientMgr.openModalWindow("dialogs/assignDomain.aspx?id=" + UmbClientMgr.mainTree().getActionNode().nodeId, uiKeys['actions_assignDomain'], true, 500, 420);
|
||||
UmbClientMgr.openModalWindow("dialogs/assignDomain2.aspx?id=" + UmbClientMgr.mainTree().getActionNode().nodeId, uiKeys['actions_assignDomain'], true, 500, 620);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
58
src/Umbraco.Web.UI/umbraco_client/Dialogs/AssignDomain2.css
Normal file
58
src/Umbraco.Web.UI/umbraco_client/Dialogs/AssignDomain2.css
Normal file
@@ -0,0 +1,58 @@
|
||||
/* Custom styles for AssignDomain2.aspx dialog */
|
||||
|
||||
button {
|
||||
font-size: 11px;
|
||||
color: #333333;
|
||||
font-family: Trebuchet MS, Lucida Grande, verdana, arial;
|
||||
}
|
||||
|
||||
table.addDomain {
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
table.domains {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table.addDomain td.help {
|
||||
padding-left:48px;
|
||||
padding-top:4px;
|
||||
}
|
||||
|
||||
button {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#komask {
|
||||
background: #ffffff;
|
||||
opacity: .6;
|
||||
z-index: 99;
|
||||
display: none;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
input.domain {
|
||||
width: 296px;
|
||||
}
|
||||
|
||||
input.domain.error {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
select.language {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
label.error {
|
||||
padding: 0 0 6px 0;
|
||||
margin: 0;
|
||||
background: none;
|
||||
border:none;
|
||||
}
|
||||
|
||||
a.remove {
|
||||
color: #ff0000;
|
||||
padding-left: 8px;
|
||||
}
|
||||
130
src/Umbraco.Web.UI/umbraco_client/Dialogs/AssignDomain2.js
Normal file
130
src/Umbraco.Web.UI/umbraco_client/Dialogs/AssignDomain2.js
Normal file
@@ -0,0 +1,130 @@
|
||||
Umbraco.Sys.registerNamespace("Umbraco.Dialogs");
|
||||
|
||||
(function ($) {
|
||||
|
||||
// register AssignDomain dialog
|
||||
Umbraco.Dialogs.AssignDomain2 = base2.Base.extend({
|
||||
|
||||
_opts: null,
|
||||
|
||||
_isRepeated: function (element) {
|
||||
var inputs = $('#form1 input.domain');
|
||||
var elementName = element.attr('name');
|
||||
var repeated = false;
|
||||
inputs.each(function() {
|
||||
var input = $(this);
|
||||
if (input.attr('name') != elementName && input.val() == element.val())
|
||||
repeated = true;
|
||||
});
|
||||
return repeated;
|
||||
},
|
||||
|
||||
// constructor
|
||||
constructor: function (opts) {
|
||||
// merge options with default
|
||||
this._opts = $.extend({
|
||||
invalidDomain: 'Invalid domain.',
|
||||
duplicateDomain: 'Domain has already been assigned.'
|
||||
}, opts);
|
||||
},
|
||||
|
||||
// public methods/variables
|
||||
|
||||
languages: null,
|
||||
language: null,
|
||||
domains: null,
|
||||
|
||||
addDomain: function () {
|
||||
this.domains.push({
|
||||
Name: "",
|
||||
Lang: ""
|
||||
});
|
||||
},
|
||||
|
||||
init: function () {
|
||||
var self = this;
|
||||
|
||||
self.domains = ko.observableArray(self._opts.domains);
|
||||
self.languages = self._opts.languages;
|
||||
self.language = self._opts.language;
|
||||
self.removeDomain = function() { self.domains.remove(this); };
|
||||
|
||||
ko.applyBindings(self);
|
||||
|
||||
$.validator.addMethod("domain", function (value, element, param) {
|
||||
var re = /^(http[s]?:\/\/)?([-\w]+(\.[-\w]+)*)(:\d+)?(\/[-\w]*)?$/gi;
|
||||
return this.optional(element) || re.test(value);
|
||||
}, self._opts.invalidDomain);
|
||||
|
||||
$.validator.addMethod("duplicate", function (value, element, param) {
|
||||
return $(element).nextAll('input').val() == 0 && !self._isRepeated($(element));
|
||||
}, self._opts.duplicateDomain);
|
||||
|
||||
$.validator.addClassRules({
|
||||
domain: { domain: true },
|
||||
duplicate: { duplicate: true }
|
||||
});
|
||||
|
||||
$('#form1').validate({
|
||||
debug: true,
|
||||
focusCleanup: true,
|
||||
onkeyup: false
|
||||
});
|
||||
|
||||
$('#form1 input.domain').live('focus', function(event) {
|
||||
if (event.type != 'focusin') return;
|
||||
$(this).nextAll('input').val(0);
|
||||
});
|
||||
|
||||
// force validation *now*
|
||||
$('#form1').valid();
|
||||
|
||||
$('#btnSave').click(function () {
|
||||
if (!$('#form1').valid())
|
||||
return false;
|
||||
|
||||
var mask = $('#komask');
|
||||
var masked = mask.next();
|
||||
mask.height(masked.height());
|
||||
mask.width(masked.width());
|
||||
mask.show();
|
||||
|
||||
var data = { nodeId: self._opts.nodeId, language: self.language ? self.language : 0, domains: self.domains };
|
||||
$.post(self._opts.restServiceLocation + 'SaveLanguageAndDomains', ko.toJSON(data), function (json) {
|
||||
mask.hide();
|
||||
|
||||
if (json.Valid) {
|
||||
UmbClientMgr.closeModalWindow();
|
||||
}
|
||||
else {
|
||||
var inputs = $('#form1 input.domain');
|
||||
inputs.each(function() { $(this).nextAll('input').val(0); });
|
||||
for (var i = 0; i < json.Domains.length; i++) {
|
||||
var d = json.Domains[i];
|
||||
if (d.Duplicate == 1)
|
||||
inputs.each(function() {
|
||||
var input = $(this);
|
||||
if (input.val() == d.Name)
|
||||
input.nextAll('input').val(1);
|
||||
});
|
||||
}
|
||||
$('#form1').valid();
|
||||
}
|
||||
})
|
||||
.fail(function (xhr, textStatus, errorThrown) {
|
||||
mask.css('opacity', 1).css('color', "#ff0000").html(xhr.responseText);
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// set defaults for jQuery ajax calls
|
||||
$.ajaxSetup({
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
contentType: 'application/json; charset=utf-8'
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
@@ -63,9 +63,9 @@ namespace Umbraco.Web.Models
|
||||
case PublishedItemType.Content:
|
||||
if (UmbracoContext.Current == null)
|
||||
throw new InvalidOperationException("Cannot resolve a Url for a content item with a null UmbracoContext.Current reference");
|
||||
if (UmbracoContext.Current.NiceUrlProvider == null)
|
||||
if (UmbracoContext.Current.UrlProvider == null)
|
||||
throw new InvalidOperationException("Cannot resolve a Url for a content item with a null UmbracoContext.Current.NiceUrlProvider reference");
|
||||
_url= UmbracoContext.Current.NiceUrlProvider.GetNiceUrl(this.Id);
|
||||
_url= UmbracoContext.Current.UrlProvider.GetUrl(this.Id);
|
||||
break;
|
||||
case PublishedItemType.Media:
|
||||
var prop = GetProperty("umbracoFile");
|
||||
|
||||
@@ -26,8 +26,8 @@ namespace Umbraco.Web.Mvc
|
||||
throw new InvalidOperationException("Cannot redirect, no entity was found for id " + _pageId);
|
||||
}
|
||||
|
||||
var result = _umbracoContext.RoutingContext.NiceUrlProvider.GetNiceUrl(PublishedContent.Id);
|
||||
if (result != NiceUrlProvider.NullUrl)
|
||||
var result = _umbracoContext.RoutingContext.UrlProvider.GetUrl(PublishedContent.Id);
|
||||
if (result != "#")
|
||||
{
|
||||
_url = result;
|
||||
return _url;
|
||||
|
||||
@@ -1173,7 +1173,7 @@ namespace Umbraco.Web
|
||||
if (firstNode == null)
|
||||
return new DataTable(); //no children found
|
||||
|
||||
var urlProvider = UmbracoContext.Current.RoutingContext.NiceUrlProvider;
|
||||
var urlProvider = UmbracoContext.Current.RoutingContext.UrlProvider;
|
||||
|
||||
//use new utility class to create table so that we don't have to maintain code in many places, just one
|
||||
var dt = Umbraco.Core.DataTableExtensions.GenerateDataTable(
|
||||
@@ -1204,7 +1204,7 @@ namespace Umbraco.Web
|
||||
{"UpdateDate", n.UpdateDate},
|
||||
{"CreatorName", n.CreatorName},
|
||||
{"WriterName", n.WriterName},
|
||||
{"Url", urlProvider.GetNiceUrl(n.Id)}
|
||||
{"Url", urlProvider.GetUrl(n.Id)}
|
||||
};
|
||||
var userVals = new Dictionary<string, object>();
|
||||
foreach (var p in from IPublishedContentProperty p in n.Properties where p.Value != null select p)
|
||||
|
||||
113
src/Umbraco.Web/Routing/AliasUrlProvider.cs
Normal file
113
src/Umbraco.Web/Routing/AliasUrlProvider.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides urls using the <c>umbracoUrlAlias</c> property.
|
||||
/// </summary>
|
||||
internal class AliasUrlProvider : IUrlProvider
|
||||
{
|
||||
// note - at the moment we seem to accept pretty much anything as an alias
|
||||
// without any form of validation ... could even prob. kill the XPath ...
|
||||
// ok, this is somewhat experimental and is NOT enabled by default
|
||||
|
||||
#region GetUrl
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nice url of a published content.
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext">The Umbraco context.</param>
|
||||
/// <param name="contentCache">The content cache.</param>
|
||||
/// <param name="id">The published content id.</param>
|
||||
/// <param name="current">The current absolute url.</param>
|
||||
/// <param name="absolute">A value indicating whether the url should be absolute in any case.</param>
|
||||
/// <returns>The url for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The url is absolute or relative depending on url indicated by <c>current</c> and settings, unless
|
||||
/// <c>absolute</c> is true, in which case the url is always absolute.</para>
|
||||
/// <para>If the provider is unable to provide a url, it should return <c>null</c>.</para>
|
||||
/// </remarks>
|
||||
public string GetUrl(UmbracoContext umbracoContext, IPublishedContentStore contentCache, int id, Uri current, bool absolute)
|
||||
{
|
||||
return null; // we have nothing to say
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetOtherUrls
|
||||
|
||||
/// <summary>
|
||||
/// Gets the other urls of a published content.
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext">The Umbraco context.</param>
|
||||
/// <param name="contentCache">The content cache.</param>
|
||||
/// <param name="id">The published content id.</param>
|
||||
/// <param name="current">The current absolute url.</param>
|
||||
/// <returns>The other urls for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Other urls are those that <c>GetUrl</c> would not return in the current context, but would be valid
|
||||
/// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...).</para>
|
||||
/// </remarks>
|
||||
public IEnumerable<string> GetOtherUrls(UmbracoContext umbracoContext, IPublishedContentStore contentCache, int id, Uri current)
|
||||
{
|
||||
if (!FindByUrlAliasEnabled)
|
||||
return Enumerable.Empty<string>(); // we have nothing to say
|
||||
|
||||
var node = contentCache.GetDocumentById(umbracoContext, id);
|
||||
string umbracoUrlName = null;
|
||||
if (node.HasProperty(UmbracoUrlAlias))
|
||||
umbracoUrlName = node.GetPropertyValue<string>(UmbracoUrlAlias);
|
||||
if (string.IsNullOrWhiteSpace(umbracoUrlName))
|
||||
return Enumerable.Empty<string>();
|
||||
|
||||
var n = node;
|
||||
var domainUris = DomainHelper.DomainsForNode(n.Id, current, false);
|
||||
while (domainUris == null && n != null) // n is null at root
|
||||
{
|
||||
// move to parent node
|
||||
n = n.Parent;
|
||||
domainUris = n == null ? null : DomainHelper.DomainsForNode(n.Id, current, false);
|
||||
}
|
||||
|
||||
var path = "/" + umbracoUrlName;
|
||||
|
||||
if (domainUris == null)
|
||||
{
|
||||
var uri = new Uri(path, UriKind.Relative);
|
||||
return new[] { UriUtility.UriFromUmbraco(uri).ToString() };
|
||||
}
|
||||
|
||||
return domainUris
|
||||
.Select(domainUri => new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path)))
|
||||
.Select(uri => UriUtility.UriFromUmbraco(uri).ToString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
const string UmbracoUrlAlias = "umbracoUrlAlias";
|
||||
|
||||
private bool FindByUrlAliasEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
var hasFinder = ContentFinderResolver.Current.ContainsType<ContentFinderByUrlAlias>();
|
||||
var hasHandler = ContentFinderResolver.Current.ContainsType<ContentFinderByNotFoundHandlers>()
|
||||
&& NotFoundHandlerHelper.CustomHandlerTypes.Contains(typeof(global::umbraco.SearchForAlias));
|
||||
return hasFinder || hasHandler;
|
||||
}
|
||||
}
|
||||
|
||||
string CombinePaths(string path1, string path2)
|
||||
{
|
||||
string path = path1.TrimEnd('/') + path2;
|
||||
return path == "/" ? path : path.TrimEnd('/');
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Xml;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
@@ -26,10 +23,10 @@ namespace Umbraco.Web.Routing
|
||||
IPublishedContent node = null;
|
||||
var path = docRequest.Uri.GetAbsolutePathDecoded();
|
||||
|
||||
int nodeId = -1;
|
||||
var nodeId = -1;
|
||||
if (path != "/") // no id if "/"
|
||||
{
|
||||
string noSlashPath = path.Substring(1);
|
||||
var noSlashPath = path.Substring(1);
|
||||
|
||||
if (!Int32.TryParse(noSlashPath, out nodeId))
|
||||
nodeId = -1;
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
@@ -37,10 +31,9 @@ namespace Umbraco.Web.Routing
|
||||
pcr.RoutingContext.UmbracoContext,
|
||||
id);
|
||||
|
||||
if (content == null)
|
||||
LogHelper.Debug<ContentFinderByLegacy404>("Could not find content with that id.");
|
||||
else
|
||||
LogHelper.Debug<ContentFinderByLegacy404>("Found corresponding content.");
|
||||
LogHelper.Debug<ContentFinderByLegacy404>(content == null
|
||||
? "Could not find content with that id."
|
||||
: "Found corresponding content.");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using System.Diagnostics;
|
||||
using System.Xml;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
@@ -97,7 +94,8 @@ namespace Umbraco.Web.Routing
|
||||
docreq.PublishedContent = node;
|
||||
LogHelper.Debug<ContentFinderByNiceUrl>("Query matches, id={0}", () => docreq.PublishedContent.Id);
|
||||
|
||||
var iscanon = _doDomainLookup && !DomainHelper.ExistsDomainInPath(docreq.Domain, node.Path);
|
||||
var rootNodeId = docreq.Domain == null ? (int?) null : docreq.Domain.RootNodeId;
|
||||
var iscanon = _doDomainLookup && !DomainHelper.ExistsDomainInPath(DomainHelper.GetAllDomains(false), node.Path, rootNodeId);
|
||||
if (!iscanon)
|
||||
LogHelper.Debug<ContentFinderByNiceUrl>("Non canonical url");
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
using System.Diagnostics;
|
||||
using System.Xml;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using umbraco.cms.businesslogic.template;
|
||||
using Umbraco.Core;
|
||||
using Template = umbraco.cms.businesslogic.template.Template;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
@@ -15,7 +11,7 @@ namespace Umbraco.Web.Routing
|
||||
/// <para>Handles <c>/foo/bar/template</c> where <c>/foo/bar</c> is the nice url of a document, and <c>template</c> a template alias.</para>
|
||||
/// <para>If successful, then the template of the document request is also assigned.</para>
|
||||
/// </remarks>
|
||||
internal class ContentFinderByNiceUrlAndTemplate : ContentFinderByNiceUrl, IContentFinder
|
||||
internal class ContentFinderByNiceUrlAndTemplate : ContentFinderByNiceUrl
|
||||
{
|
||||
/// <summary>
|
||||
/// Tries to find and assign an Umbraco document to a <c>PublishedContentRequest</c>.
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
using umbraco;
|
||||
using umbraco.interfaces;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
@@ -16,7 +8,7 @@ namespace Umbraco.Web.Routing
|
||||
/// Provides an implementation of <see cref="IContentFinder"/> that runs a legacy NotFoundHandler.
|
||||
/// </summary>
|
||||
/// <remarks>Provided for backward compatibility.</remarks>
|
||||
internal class ContentFinderByNotFoundHandler<Thandler> : IContentFinder
|
||||
internal class ContentFinderByNotFoundHandler<THandler> : IContentFinder
|
||||
{
|
||||
/// <summary>
|
||||
/// Tries to find and assign an Umbraco document to a <c>PublishedContentRequest</c>.
|
||||
@@ -25,36 +17,33 @@ namespace Umbraco.Web.Routing
|
||||
/// <returns>A value indicating whether an Umbraco document was found and assigned.</returns>
|
||||
public bool TryFindDocument(PublishedContentRequest pcr)
|
||||
{
|
||||
var type = typeof(Thandler);
|
||||
var type = typeof(THandler);
|
||||
var handler = GetHandler(type);
|
||||
|
||||
if (handler == null)
|
||||
return false;
|
||||
|
||||
var url = NotFoundHandlerHelper.GetLegacyUrlForNotFoundHandlers();
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandler<Thandler>>("Running for legacy url='{0}'.", () => url);
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandler<THandler>>("Running for legacy url='{0}'.", () => url);
|
||||
|
||||
if (handler.Execute(url) && handler.redirectID > 0)
|
||||
{
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandler<Thandler>>("Handler '{0}' returned id={1}.", () => type.FullName, () => handler.redirectID);
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandler<THandler>>("Handler '{0}' returned id={1}.", () => type.FullName, () => handler.redirectID);
|
||||
|
||||
var content = pcr.RoutingContext.PublishedContentStore.GetDocumentById(
|
||||
pcr.RoutingContext.UmbracoContext,
|
||||
handler.redirectID);
|
||||
|
||||
if (content == null)
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandler<Thandler>>("Could not find content with that id.");
|
||||
else
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandler<Thandler>>("Found corresponding content.");
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandler<THandler>>(content == null
|
||||
? "Could not find content with that id."
|
||||
: "Found corresponding content.");
|
||||
|
||||
pcr.PublishedContent = content;
|
||||
pcr.PublishedContent = content;
|
||||
return content != null;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandler<Thandler>>("Handler '{0}' returned nothing.", () => type.FullName);
|
||||
return false;
|
||||
}
|
||||
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandler<THandler>>("Handler '{0}' returned nothing.", () => type.FullName);
|
||||
return false;
|
||||
}
|
||||
|
||||
INotFoundHandler GetHandler(Type type)
|
||||
@@ -65,7 +54,7 @@ namespace Umbraco.Web.Routing
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogHelper.Error<ContentFinderByNotFoundHandler<Thandler>>(string.Format("Error instanciating handler {0}, ignoring.", type.FullName), e);
|
||||
LogHelper.Error<ContentFinderByNotFoundHandler<THandler>>(string.Format("Error instanciating handler {0}, ignoring.", type.FullName), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Web;
|
||||
using System.Xml;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using umbraco.IO;
|
||||
using umbraco.interfaces;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
@@ -35,8 +29,6 @@ namespace Umbraco.Web.Routing
|
||||
|
||||
#region Copied over and adapted from presentation.requestHandler
|
||||
|
||||
//FIXME: this is temporary and should be obsoleted
|
||||
|
||||
void HandlePageNotFound(PublishedContentRequest docRequest)
|
||||
{
|
||||
var url = NotFoundHandlerHelper.GetLegacyUrlForNotFoundHandlers();
|
||||
@@ -45,8 +37,9 @@ namespace Umbraco.Web.Routing
|
||||
foreach (var handler in GetNotFoundHandlers())
|
||||
{
|
||||
IContentFinder finder = null;
|
||||
var handlerName = handler.GetType().FullName;
|
||||
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Handler '{0}'.", () => handler.GetType().FullName);
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Handler '{0}'.", () => handlerName);
|
||||
|
||||
// replace with our own implementation
|
||||
if (handler is global::umbraco.SearchForAlias)
|
||||
@@ -60,14 +53,15 @@ namespace Umbraco.Web.Routing
|
||||
|
||||
if (finder != null)
|
||||
{
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Replace handler '{0}' by new finder '{1}'.", () => handler.GetType().FullName, () => finder.GetType().FullName);
|
||||
var finderName = finder.GetType().FullName;
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Replace handler '{0}' by new finder '{1}'.", () => handlerName, () => finderName);
|
||||
if (finder.TryFindDocument(docRequest))
|
||||
{
|
||||
// do NOT set docRequest.PublishedContent again here as
|
||||
// it would clear any template that the finder might have set
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Finder '{0}' found node with id={1}.", () => finder.GetType().FullName, () => docRequest.PublishedContent.Id);
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Finder '{0}' found node with id={1}.", () => finderName, () => docRequest.PublishedContent.Id);
|
||||
if (docRequest.Is404)
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Finder '{0}' set status to 404.", () => finder.GetType().FullName);
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Finder '{0}' set status to 404.", () => finderName);
|
||||
|
||||
// if we found a document, break, don't look at more handler -- we're done
|
||||
break;
|
||||
@@ -80,22 +74,23 @@ namespace Umbraco.Web.Routing
|
||||
// else it's a legacy handler, run
|
||||
|
||||
if (handler.Execute(url) && handler.redirectID > 0)
|
||||
{
|
||||
{
|
||||
var redirectId = handler.redirectID;
|
||||
docRequest.PublishedContent = docRequest.RoutingContext.PublishedContentStore.GetDocumentById(
|
||||
docRequest.RoutingContext.UmbracoContext,
|
||||
handler.redirectID);
|
||||
redirectId);
|
||||
|
||||
if (!docRequest.HasPublishedContent)
|
||||
{
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Handler '{0}' found node with id={1} which is not valid.", () => handler.GetType().FullName, () => handler.redirectID);
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Handler '{0}' found node with id={1} which is not valid.", () => handlerName, () => redirectId);
|
||||
break;
|
||||
}
|
||||
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Handler '{0}' found valid node with id={1}.", () => handler.GetType().FullName, () => handler.redirectID);
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Handler '{0}' found valid node with id={1}.", () => handlerName, () => redirectId);
|
||||
|
||||
if (docRequest.RoutingContext.UmbracoContext.HttpContext.Response.StatusCode == 404)
|
||||
{
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Handler '{0}' set status code to 404.", () => handler.GetType().FullName);
|
||||
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Handler '{0}' set status code to 404.", () => handlerName);
|
||||
docRequest.Is404 = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using System.Diagnostics;
|
||||
using System.Xml;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using umbraco;
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using System.Diagnostics;
|
||||
using System.Xml;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
|
||||
262
src/Umbraco.Web/Routing/DefaultUrlProvider.cs
Normal file
262
src/Umbraco.Web/Routing/DefaultUrlProvider.cs
Normal file
@@ -0,0 +1,262 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using umbraco.cms.businesslogic.web;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides urls.
|
||||
/// </summary>
|
||||
internal class DefaultUrlProvider : IUrlProvider
|
||||
{
|
||||
#region GetUrl
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nice url of a published content.
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext">The Umbraco context.</param>
|
||||
/// <param name="contentCache">The content cache.</param>
|
||||
/// <param name="id">The published content id.</param>
|
||||
/// <param name="current">The current absolute url.</param>
|
||||
/// <param name="absolute">A value indicating whether the url should be absolute in any case.</param>
|
||||
/// <returns>The url for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The url is absolute or relative depending on url indicated by <c>current</c> and settings, unless
|
||||
/// <c>absolute</c> is true, in which case the url is always absolute.</para>
|
||||
/// <para>If the provider is unable to provide a url, it should return <c>null</c>.</para>
|
||||
/// </remarks>
|
||||
public virtual string GetUrl(UmbracoContext umbracoContext, IPublishedContentStore contentCache, int id, Uri current, bool absolute)
|
||||
{
|
||||
DomainAndUri domainUri;
|
||||
string path;
|
||||
|
||||
if (!current.IsAbsoluteUri)
|
||||
// ReSharper disable LocalizableElement
|
||||
throw new ArgumentException("Current url must be absolute.", "current");
|
||||
// ReSharper restore LocalizableElement
|
||||
|
||||
// do not read cache if previewing
|
||||
var route = umbracoContext.InPreviewMode
|
||||
? null
|
||||
: umbracoContext.RoutingContext.RoutesCache.GetRoute(id);
|
||||
|
||||
if (!string.IsNullOrEmpty(route))
|
||||
{
|
||||
// there was a route in the cache - extract domainUri and path
|
||||
// route is /<path> or <domainRootId>/<path>
|
||||
int pos = route.IndexOf('/');
|
||||
path = pos == 0 ? route : route.Substring(pos);
|
||||
domainUri = pos == 0 ? null : DomainHelper.DomainForNode(int.Parse(route.Substring(0, pos)), current);
|
||||
}
|
||||
else
|
||||
{
|
||||
// there was no route in the cache - create a route
|
||||
var node = contentCache.GetDocumentById(umbracoContext, id);
|
||||
if (node == null)
|
||||
{
|
||||
LogHelper.Warn<DefaultUrlProvider>(
|
||||
"Couldn't find any page with nodeId={0}. This is most likely caused by the page not being published.",
|
||||
() => id);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// walk up from that node until we hit a node with a domain,
|
||||
// or we reach the content root, collecting urls in the way
|
||||
var pathParts = new List<string>();
|
||||
var n = node;
|
||||
domainUri = DomainHelper.DomainForNode(n.Id, current);
|
||||
while (domainUri == null && n != null) // n is null at root
|
||||
{
|
||||
// get the url
|
||||
var urlName = n.UrlName;
|
||||
pathParts.Add(urlName);
|
||||
|
||||
// move to parent node
|
||||
n = n.Parent;
|
||||
domainUri = n == null ? null : DomainHelper.DomainForNode(n.Id, current);
|
||||
}
|
||||
|
||||
// no domain, respect HideTopLevelNodeFromPath for legacy purposes
|
||||
if (domainUri == null && global::umbraco.GlobalSettings.HideTopLevelNodeFromPath)
|
||||
ApplyHideTopLevelNodeFromPath(umbracoContext, contentCache, node, pathParts);
|
||||
|
||||
// assemble the route
|
||||
pathParts.Reverse();
|
||||
path = "/" + string.Join("/", pathParts); // will be "/" or "/foo" or "/foo/bar" etc
|
||||
route = (n == null ? "" : n.Id.ToString()) + path;
|
||||
|
||||
// do not store if previewing
|
||||
if (!umbracoContext.InPreviewMode)
|
||||
umbracoContext.RoutingContext.RoutesCache.Store(id, route);
|
||||
}
|
||||
|
||||
// assemble the url from domainUri (maybe null) and path
|
||||
return AssembleUrl(domainUri, path, current, absolute).ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetOtherUrls
|
||||
|
||||
/// <summary>
|
||||
/// Gets the other urls of a published content.
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext">The Umbraco context.</param>
|
||||
/// <param name="contentCache">The content cache.</param>
|
||||
/// <param name="id">The published content id.</param>
|
||||
/// <param name="current">The current absolute url.</param>
|
||||
/// <returns>The other urls for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Other urls are those that <c>GetUrl</c> would not return in the current context, but would be valid
|
||||
/// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...).</para>
|
||||
/// </remarks>
|
||||
public virtual IEnumerable<string> GetOtherUrls(UmbracoContext umbracoContext, IPublishedContentStore contentCache, int id, Uri current)
|
||||
{
|
||||
string path;
|
||||
IEnumerable<DomainAndUri> domainUris;
|
||||
|
||||
// will not read cache if previewing!
|
||||
var route = umbracoContext.InPreviewMode
|
||||
? null
|
||||
: umbracoContext.RoutingContext.RoutesCache.GetRoute(id);
|
||||
|
||||
if (!string.IsNullOrEmpty(route))
|
||||
{
|
||||
// there was a route in the cache - extract domainUri and path
|
||||
// route is /<path> or <domainRootId>/<path>
|
||||
int pos = route.IndexOf('/');
|
||||
path = pos == 0 ? route : route.Substring(pos);
|
||||
domainUris = pos == 0 ? null : DomainHelper.DomainsForNode(int.Parse(route.Substring(0, pos)), current);
|
||||
}
|
||||
else
|
||||
{
|
||||
// there was no route in the cache - create a route
|
||||
var node = contentCache.GetDocumentById(umbracoContext, id);
|
||||
if (node == null)
|
||||
{
|
||||
LogHelper.Warn<DefaultUrlProvider>(
|
||||
"Couldn't find any page with nodeId={0}. This is most likely caused by the page not being published.",
|
||||
() => id);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// walk up from that node until we hit a node with domains,
|
||||
// or we reach the content root, collecting urls in the way
|
||||
var pathParts = new List<string>();
|
||||
var n = node;
|
||||
domainUris = DomainHelper.DomainsForNode(n.Id, current);
|
||||
while (domainUris == null && n != null) // n is null at root
|
||||
{
|
||||
// get the url
|
||||
var urlName = node.UrlName;
|
||||
pathParts.Add(urlName);
|
||||
|
||||
// move to parent node
|
||||
n = n.Parent;
|
||||
domainUris = n == null ? null : DomainHelper.DomainsForNode(n.Id, current);
|
||||
}
|
||||
|
||||
// no domain, respect HideTopLevelNodeFromPath for legacy purposes
|
||||
if (domainUris == null && global::umbraco.GlobalSettings.HideTopLevelNodeFromPath)
|
||||
ApplyHideTopLevelNodeFromPath(umbracoContext, contentCache, node, pathParts);
|
||||
|
||||
// assemble the route
|
||||
pathParts.Reverse();
|
||||
path = "/" + string.Join("/", pathParts); // will be "/" or "/foo" or "/foo/bar" etc
|
||||
route = (n == null ? "" : n.Id.ToString()) + path;
|
||||
|
||||
// do not store if previewing
|
||||
if (!umbracoContext.InPreviewMode)
|
||||
umbracoContext.RoutingContext.RoutesCache.Store(id, route);
|
||||
}
|
||||
|
||||
// assemble the alternate urls from domainUris (maybe empty) and path
|
||||
return AssembleUrls(domainUris, path).Select(uri => uri.ToString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
Uri AssembleUrl(DomainAndUri domainUri, string path, Uri current, bool absolute)
|
||||
{
|
||||
Uri uri;
|
||||
|
||||
if (domainUri == null)
|
||||
{
|
||||
// no domain was found : return an absolute or relative url
|
||||
// ignore vdir at that point
|
||||
if (!absolute || current == null)
|
||||
uri = new Uri(path, UriKind.Relative);
|
||||
else
|
||||
uri = new Uri(current.GetLeftPart(UriPartial.Authority) + path);
|
||||
}
|
||||
else
|
||||
{
|
||||
// a domain was found : return an absolute or relative url
|
||||
// ignore vdir at that point
|
||||
if (!absolute && current != null && domainUri.Uri.GetLeftPart(UriPartial.Authority) == current.GetLeftPart(UriPartial.Authority))
|
||||
uri = new Uri(CombinePaths(domainUri.Uri.AbsolutePath, path), UriKind.Relative); // relative
|
||||
else
|
||||
uri = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path)); // absolute
|
||||
}
|
||||
|
||||
// UriFromUmbraco will handle vdir
|
||||
// meaning it will add vdir into domain urls too!
|
||||
return UriUtility.UriFromUmbraco(uri);
|
||||
}
|
||||
|
||||
string CombinePaths(string path1, string path2)
|
||||
{
|
||||
string path = path1.TrimEnd('/') + path2;
|
||||
return path == "/" ? path : path.TrimEnd('/');
|
||||
}
|
||||
|
||||
// always build absolute urls unless we really cannot
|
||||
IEnumerable<Uri> AssembleUrls(IEnumerable<DomainAndUri> domainUris, string path)
|
||||
{
|
||||
// no domain == no "other" url
|
||||
if (domainUris == null)
|
||||
return Enumerable.Empty<Uri>();
|
||||
|
||||
// if no domain was found and then we have no "other" url
|
||||
// else return absolute urls, ignoring vdir at that point
|
||||
var uris = domainUris.Select(domainUri => new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path)));
|
||||
|
||||
// UriFromUmbraco will handle vdir
|
||||
// meaning it will add vdir into domain urls too!
|
||||
return uris.Select(UriUtility.UriFromUmbraco);
|
||||
}
|
||||
|
||||
static void ApplyHideTopLevelNodeFromPath(UmbracoContext umbracoContext, IPublishedContentStore contentCache, Core.Models.IPublishedContent node, IList<string> pathParts)
|
||||
{
|
||||
// in theory if hideTopLevelNodeFromPath is true, then there should be only once
|
||||
// top-level node, or else domains should be assigned. but for backward compatibility
|
||||
// we add this check - we look for the document matching "/" and if it's not us, then
|
||||
// we do not hide the top level path
|
||||
// it has to be taken care of in IPublishedContentStore.GetDocumentByRoute too so if
|
||||
// "/foo" fails (looking for "/*/foo") we try also "/foo".
|
||||
// this does not make much sense anyway esp. if both "/foo/" and "/bar/foo" exist, but
|
||||
// that's the way it works pre-4.10 and we try to be backward compat for the time being
|
||||
if (node.Parent == null)
|
||||
{
|
||||
var rootNode = contentCache.GetDocumentByRoute(umbracoContext, "/", true);
|
||||
if (rootNode.Id == node.Id) // remove only if we're the default node
|
||||
pathParts.RemoveAt(pathParts.Count - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
pathParts.RemoveAt(pathParts.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
45
src/Umbraco.Web/Routing/DomainAndUri.cs
Normal file
45
src/Umbraco.Web/Routing/DomainAndUri.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using umbraco.cms.businesslogic.web;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an Umbraco domain and its normalized uri.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>In Umbraco it is valid to create domains with name such as <c>example.com</c>, <c>https://www.example.com</c>, <c>example.com/foo/</c>.</para>
|
||||
/// <para>The normalized uri of a domain begins with a scheme and ends with no slash, eg <c>http://example.com/</c>, <c>https://www.example.com/</c>, <c>http://example.com/foo/</c>.</para>
|
||||
/// </remarks>
|
||||
internal class DomainAndUri
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DomainAndUri"/> class with a Domain and a uri scheme.
|
||||
/// </summary>
|
||||
/// <param name="domain">The domain.</param>
|
||||
/// <param name="scheme">The uri scheme.</param>
|
||||
public DomainAndUri(Domain domain, string scheme)
|
||||
{
|
||||
Domain = domain;
|
||||
Uri = new Uri(UriUtility.TrimPathEndSlash(UriUtility.StartWithScheme(domain.Name, scheme)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Umbraco domain.
|
||||
/// </summary>
|
||||
public Domain Domain { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the normalized uri of the domain.
|
||||
/// </summary>
|
||||
public Uri Uri { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string that represents the <see cref="DomainAndUri"/> instance.
|
||||
/// </summary>
|
||||
/// <returns>A string that represents the current <see cref="DomainAndUri"/> instance.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{{ \"{0}\", \"{1}\" }}", Domain.Name, Uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,185 +1,272 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using System.Threading;
|
||||
using Umbraco.Core;
|
||||
using umbraco.cms.businesslogic.web;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utilities to handle domains.
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// Provides utilities to handle domains.
|
||||
/// </summary>
|
||||
internal class DomainHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an Umbraco domain and its normalized uri.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>In Umbraco it is valid to create domains with name such as <c>example.com</c>, <c>https://www.example.com</c>, <c>example.com/foo/</c>.</para>
|
||||
/// <para>The normalized uri of a domain begins with a scheme and ends with no slash, eg <c>http://example.com/</c>, <c>https://www.example.com/</c>, <c>http://example.com/foo/</c>.</para>
|
||||
/// </remarks>
|
||||
internal class DomainAndUri
|
||||
{
|
||||
/// <summary>
|
||||
/// The Umbraco domain.
|
||||
/// </summary>
|
||||
public Domain Domain;
|
||||
#region Temp. abstract Umbraco's API
|
||||
|
||||
/// <summary>
|
||||
/// The normalized uri of the domain.
|
||||
/// </summary>
|
||||
public Uri Uri;
|
||||
/// <summary>
|
||||
/// Gets all domains defined in the system.
|
||||
/// </summary>
|
||||
/// <param name="includeWildcards">A value indicating whether to include wildcard domains.</param>
|
||||
/// <returns>All domains defined in the system.</returns>
|
||||
/// <remarks>This is to temporarily abstract Umbraco's API.</remarks>
|
||||
internal static Domain[] GetAllDomains(bool includeWildcards)
|
||||
{
|
||||
return Domain.GetDomains(includeWildcards).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string that represents the <see cref="DomainAndUri"/> instance.
|
||||
/// </summary>
|
||||
/// <returns>A string that represents the current <see cref="DomainAndUri"/> instance.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{{ \"{0}\", \"{1}\" }}", Domain.Name, Uri);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets all domains defined in the system at a specified node.
|
||||
/// </summary>
|
||||
/// <param name="nodeId">The node identifier.</param>
|
||||
/// <param name="includeWildcards">A value indicating whether to include wildcard domains.</param>
|
||||
/// <returns>All domains defined in the system at the specified node.</returns>
|
||||
/// <remarks>This is to temporarily abstract Umbraco's API.</remarks>
|
||||
internal static Domain[] GetNodeDomains(int nodeId, bool includeWildcards)
|
||||
{
|
||||
return Domain.GetDomains(includeWildcards).Where(d => d.RootNodeId == nodeId).ToArray();
|
||||
}
|
||||
|
||||
private static bool IsWildcardDomain(Domain d)
|
||||
{
|
||||
// supporting null or whitespace for backward compatibility,
|
||||
// although we should not allow ppl to create them anymore
|
||||
return string.IsNullOrWhiteSpace(d.Name) || d.Name.StartsWith("*");
|
||||
}
|
||||
#endregion
|
||||
|
||||
private static Domain SanitizeForBackwardCompatibility(Domain d)
|
||||
{
|
||||
// this is a _really_ nasty one that should be removed in 6.x
|
||||
// some people were using hostnames such as "/en" which happened to work pre-4.10
|
||||
// but make _no_ sense at all... and 4.10 throws on them, so here we just try
|
||||
// to find a way so 4.11 does not throw.
|
||||
// but, really.
|
||||
// no.
|
||||
var context = System.Web.HttpContext.Current;
|
||||
if (context != null && d.Name.StartsWith("/"))
|
||||
{
|
||||
// turn /en into http://whatever.com/en so it becomes a parseable uri
|
||||
var authority = context.Request.Url.GetLeftPart(UriPartial.Authority);
|
||||
d.Name = authority + d.Name;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
#region Domain for Node
|
||||
|
||||
/// <summary>
|
||||
/// Finds the domain that best matches the current uri, into an enumeration of domains.
|
||||
/// </summary>
|
||||
/// <param name="domains">The enumeration of Umbraco domains.</param>
|
||||
/// <param name="current">The uri of the current request, or null.</param>
|
||||
/// <param name="defaultToFirst">A value indicating whether to return the first domain of the list when no domain matches.</param>
|
||||
/// <returns>The domain and its normalized uri, that best matches the current uri, else the first domain (if <c>defaultToFirst</c> is <c>true</c>), else null.</returns>
|
||||
public static DomainAndUri DomainMatch(IEnumerable<Domain> domains, Uri current, bool defaultToFirst)
|
||||
{
|
||||
// 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
|
||||
.Where(d => !IsWildcardDomain(d))
|
||||
.Select(d => SanitizeForBackwardCompatibility(d))
|
||||
.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) });
|
||||
/// <summary>
|
||||
/// Finds the domain for the specified node, if any, that best matches a specified uri.
|
||||
/// </summary>
|
||||
/// <param name="nodeId">The node identifier.</param>
|
||||
/// <param name="current">The uri, or null.</param>
|
||||
/// <returns>The domain and its uri, if any, that best matches the specified uri, else null.</returns>
|
||||
/// <remarks>If at least a domain is set on the node then the method returns the domain that
|
||||
/// best matches the specified uri, else it returns null.</remarks>
|
||||
internal static DomainAndUri DomainForNode(int nodeId, Uri current)
|
||||
{
|
||||
// be safe
|
||||
if (nodeId <= 0)
|
||||
return null;
|
||||
|
||||
if (!domainsAndUris.Any())
|
||||
return null;
|
||||
// get the domains on that node
|
||||
var domains = GetNodeDomains(nodeId, false);
|
||||
|
||||
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();
|
||||
}
|
||||
// none?
|
||||
if (!domains.Any())
|
||||
return null;
|
||||
|
||||
if (domainAndUri != null)
|
||||
domainAndUri.Uri = domainAndUri.Uri.TrimPathEndSlash();
|
||||
return domainAndUri;
|
||||
}
|
||||
// else filter
|
||||
var helper = SiteDomainHelperResolver.Current.Helper;
|
||||
var domainAndUri = DomainForUri(domains, current, domainAndUris => helper.MapDomain(current, domainAndUris));
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumeration of <see cref="DomainAndUri"/> matching an enumeration of Umbraco domains.
|
||||
/// </summary>
|
||||
/// <param name="domains">The enumeration of Umbraco domains.</param>
|
||||
/// <param name="current">The uri of the current request, or null.</param>
|
||||
/// <returns>The enumeration of <see cref="DomainAndUri"/> matching the enumeration of Umbraco domains.</returns>
|
||||
public static IEnumerable<DomainAndUri> DomainMatches(IEnumerable<Domain> domains, Uri current)
|
||||
{
|
||||
var scheme = current == null ? Uri.UriSchemeHttp : current.Scheme;
|
||||
var domainsAndUris = domains
|
||||
.Where(d => !IsWildcardDomain(d))
|
||||
.Select(d => SanitizeForBackwardCompatibility(d))
|
||||
.Select(d => new { Domain = d, UriString = UriUtility.TrimPathEndSlash(UriUtility.StartWithScheme(d.Name, scheme)) })
|
||||
.OrderByDescending(t => t.UriString)
|
||||
.Select(t => new DomainAndUri { Domain = t.Domain, Uri = new Uri(t.UriString) });
|
||||
return domainsAndUris;
|
||||
}
|
||||
if (domainAndUri == null)
|
||||
throw new Exception("DomainForUri returned null.");
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether there is another domain defined down in the path to a node under the current domain's root node.
|
||||
/// </summary>
|
||||
/// <param name="current">The current domain.</param>
|
||||
/// <param name="path">The path to a node under the current domain's root node.</param>
|
||||
/// <returns>A value indicating if there is another domain defined down in the path.</returns>
|
||||
public static bool ExistsDomainInPath(Domain current, string path)
|
||||
{
|
||||
var domains = Domain.GetDomains();
|
||||
var stopNodeId = current == null ? -1 : current.RootNodeId;
|
||||
return domainAndUri;
|
||||
}
|
||||
|
||||
return path.Split(',')
|
||||
.Reverse()
|
||||
.Select(id => int.Parse(id))
|
||||
.TakeWhile(id => id != stopNodeId)
|
||||
.Any(id => domains.Any(d => d.RootNodeId == id && !IsWildcardDomain(d)));
|
||||
}
|
||||
/// <summary>
|
||||
/// Find the domains for the specified node, if any, that match a specified uri.
|
||||
/// </summary>
|
||||
/// <param name="nodeId">The node identifier.</param>
|
||||
/// <param name="current">The uri, or null.</param>
|
||||
/// <param name="excludeDefault">A value indicating whether to exclude the current/default domain. True by default.</param>
|
||||
/// <returns>The domains and their uris, that match the specified uri, else null.</returns>
|
||||
/// <remarks>If at least a domain is set on the node then the method returns the domains that
|
||||
/// best match the specified uri, else it returns null.</remarks>
|
||||
internal static IEnumerable<DomainAndUri> DomainsForNode(int nodeId, Uri current, bool excludeDefault = true)
|
||||
{
|
||||
// be safe
|
||||
if (nodeId <= 0)
|
||||
return null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the deepest wildcard <see cref="Domain"/> in a node path.
|
||||
/// </summary>
|
||||
/// <param name="domains">The enumeration of Umbraco domains.</param>
|
||||
/// <param name="path">The node path.</param>
|
||||
/// <param name="rootNodeId">The current domain root node identifier, or null.</param>
|
||||
/// <returns>The deepest wildcard <see cref="Domain"/> in the path, or null.</returns>
|
||||
public static Domain LookForWildcardDomain(IEnumerable<Domain> domains, string path, int? rootNodeId)
|
||||
{
|
||||
// "When you perform comparisons with nullable types, if the value of one of the nullable
|
||||
// types is null and the other is not, all comparisons evaluate to false."
|
||||
// get the domains on that node
|
||||
var domains = GetNodeDomains(nodeId, false);
|
||||
|
||||
return path
|
||||
.Split(',')
|
||||
.Select(int.Parse)
|
||||
.Skip(1)
|
||||
.Reverse()
|
||||
.TakeWhile(id => !rootNodeId.HasValue || id != rootNodeId)
|
||||
.Select(nodeId => domains.FirstOrDefault(d => d.RootNodeId == nodeId && IsWildcardDomain(d)))
|
||||
.FirstOrDefault(domain => domain != null);
|
||||
}
|
||||
// none?
|
||||
if (!domains.Any())
|
||||
return null;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the part of a path relative to the uri of a domain.
|
||||
/// </summary>
|
||||
/// <param name="domainUri">The normalized uri of the domain.</param>
|
||||
/// <param name="path">The full path of the uri.</param>
|
||||
/// <returns>The path part relative to the uri of the domain.</returns>
|
||||
/// <remarks>Eg the relative part of <c>/foo/bar/nil</c> to domain <c>example.com/foo</c> is <c>/bar/nil</c>.</remarks>
|
||||
public static string PathRelativeToDomain(Uri domainUri, string path)
|
||||
{
|
||||
return path.Substring(domainUri.AbsolutePath.Length).EnsureStartsWith('/');
|
||||
}
|
||||
}
|
||||
// get the domains and their uris
|
||||
var domainAndUris = DomainsForUri(domains, current).ToArray();
|
||||
|
||||
// filter
|
||||
var helper = SiteDomainHelperResolver.Current.Helper;
|
||||
return helper.MapDomains(current, domainAndUris, excludeDefault).ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Domain for Uri
|
||||
|
||||
/// <summary>
|
||||
/// Finds the domain that best matches a specified uri, into a group of domains.
|
||||
/// </summary>
|
||||
/// <param name="domains">The group of domains.</param>
|
||||
/// <param name="current">The uri, or null.</param>
|
||||
/// <param name="filter">A function to filter the list of domains, if more than one applies, or <c>null</c>.</param>
|
||||
/// <returns>The domain and its normalized uri, that best matches the specified uri.</returns>
|
||||
/// <remarks>
|
||||
/// <para>If more than one domain matches, then the <paramref name="filter"/> function is used to pick
|
||||
/// the right one, unless it is <c>null</c>, in which case the method returns <c>null</c>.</para>
|
||||
/// <para>The filter, if any, will be called only with a non-empty argument, and _must_ return something.</para>
|
||||
/// </remarks>
|
||||
internal static DomainAndUri DomainForUri(Domain[] domains, Uri current, Func<DomainAndUri[], DomainAndUri> filter = 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
|
||||
.Where(d => !d.IsWildcard)
|
||||
.Select(SanitizeForBackwardCompatibility)
|
||||
.Select(d => new DomainAndUri(d, scheme))
|
||||
.OrderByDescending(d => d.Uri.ToString())
|
||||
.ToArray();
|
||||
|
||||
if (!domainsAndUris.Any())
|
||||
return null;
|
||||
|
||||
DomainAndUri domainAndUri;
|
||||
if (current == null)
|
||||
{
|
||||
// take the first one by default (what else can we do?)
|
||||
domainAndUri = domainsAndUris.First(); // .First() protected by .Any() above
|
||||
}
|
||||
else
|
||||
{
|
||||
// look for the first domain that would be the base of the hint
|
||||
var hintWithSlash = current.EndPathWithSlash();
|
||||
domainAndUri = domainsAndUris
|
||||
.FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(hintWithSlash));
|
||||
// if none matches, then try to run the filter to pick a domain
|
||||
if (domainAndUri == null && filter != null)
|
||||
{
|
||||
domainAndUri = filter(domainsAndUris);
|
||||
// if still nothing, pick the first one?
|
||||
// no: move that constraint to the filter, but check
|
||||
if (domainAndUri == null)
|
||||
throw new InvalidOperationException("The filter returned null.");
|
||||
}
|
||||
}
|
||||
|
||||
return domainAndUri;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the domains that match a specified uri, into a group of domains.
|
||||
/// </summary>
|
||||
/// <param name="domains">The group of domains.</param>
|
||||
/// <param name="current">The uri, or null.</param>
|
||||
/// <returns>The domains and their normalized uris, that match the specified uri.</returns>
|
||||
internal static IEnumerable<DomainAndUri> DomainsForUri(Domain[] domains, Uri current)
|
||||
{
|
||||
var scheme = current == null ? Uri.UriSchemeHttp : current.Scheme;
|
||||
return domains
|
||||
.Where(d => !d.IsWildcard)
|
||||
.Select(SanitizeForBackwardCompatibility)
|
||||
.Select(d => new DomainAndUri(d, scheme))
|
||||
.OrderByDescending(d => d.Uri.ToString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Sanitize a Domain.
|
||||
/// </summary>
|
||||
/// <param name="domain">The Domain to sanitize.</param>
|
||||
/// <returns>The sanitized domain.</returns>
|
||||
/// <remarks>This is a _really_ nasty one that should be removed at some point. Some people were
|
||||
/// using hostnames such as "/en" which happened to work pre-4.10 but really make no sense at
|
||||
/// all... and 4.10 throws on them, so here we just try to find a way so 4.11 does not throw.
|
||||
/// But really... no.</remarks>
|
||||
private static Domain SanitizeForBackwardCompatibility(Domain domain)
|
||||
{
|
||||
var context = System.Web.HttpContext.Current;
|
||||
if (context != null && domain.Name.StartsWith("/"))
|
||||
{
|
||||
// turn "/en" into "http://whatever.com/en" so it becomes a parseable uri
|
||||
var authority = context.Request.Url.GetLeftPart(UriPartial.Authority);
|
||||
domain.Name = authority + domain.Name;
|
||||
}
|
||||
return domain;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether there is another domain defined down in the path to a node under the current domain's root node.
|
||||
/// </summary>
|
||||
/// <param name="domains">The domains.</param>
|
||||
/// <param name="path">The path to a node under the current domain's root node eg '-1,1234,5678'.</param>
|
||||
/// <param name="rootNodeId">The current domain root node identifier, or null.</param>
|
||||
/// <returns>A value indicating if there is another domain defined down in the path.</returns>
|
||||
/// <remarks>Looks _under_ rootNodeId but not _at_ rootNodeId.</remarks>
|
||||
internal static bool ExistsDomainInPath(Domain[] domains, string path, int? rootNodeId)
|
||||
{
|
||||
return FindDomainInPath(domains, path, rootNodeId) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the deepest non-wildcard Domain, if any, from a group of Domains, in a node path.
|
||||
/// </summary>
|
||||
/// <param name="domains">The domains.</param>
|
||||
/// <param name="path">The node path eg '-1,1234,5678'.</param>
|
||||
/// <param name="rootNodeId">The current domain root node identifier, or null.</param>
|
||||
/// <returns>The deepest non-wildcard Domain in the path, or null.</returns>
|
||||
/// <remarks>Looks _under_ rootNodeId but not _at_ rootNodeId.</remarks>
|
||||
internal static Domain FindDomainInPath(Domain[] domains, string path, int? rootNodeId)
|
||||
{
|
||||
var stopNodeId = rootNodeId ?? -1;
|
||||
|
||||
return path.Split(',')
|
||||
.Reverse()
|
||||
.Select(int.Parse)
|
||||
.TakeWhile(id => id != stopNodeId)
|
||||
.Select(id => domains.FirstOrDefault(d => d.RootNodeId == id && !d.IsWildcard))
|
||||
.SkipWhile(domain => domain == null)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the deepest wildcard Domain, if any, from a group of Domains, in a node path.
|
||||
/// </summary>
|
||||
/// <param name="domains">The domains.</param>
|
||||
/// <param name="path">The node path eg '-1,1234,5678'.</param>
|
||||
/// <param name="rootNodeId">The current domain root node identifier, or null.</param>
|
||||
/// <returns>The deepest wildcard Domain in the path, or null.</returns>
|
||||
/// <remarks>Looks _under_ rootNodeId but not _at_ rootNodeId.</remarks>
|
||||
internal static Domain FindWildcardDomainInPath(Domain[] domains, string path, int? rootNodeId)
|
||||
{
|
||||
var stopNodeId = rootNodeId ?? -1;
|
||||
|
||||
return path.Split(',')
|
||||
.Reverse()
|
||||
.Select(int.Parse)
|
||||
.TakeWhile(id => id != stopNodeId)
|
||||
.Select(id => domains.FirstOrDefault(d => d.RootNodeId == id && d.IsWildcard))
|
||||
.FirstOrDefault(domain => domain != null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the part of a path relative to the uri of a domain.
|
||||
/// </summary>
|
||||
/// <param name="domainUri">The normalized uri of the domain.</param>
|
||||
/// <param name="path">The full path of the uri.</param>
|
||||
/// <returns>The path part relative to the uri of the domain.</returns>
|
||||
/// <remarks>Eg the relative part of <c>/foo/bar/nil</c> to domain <c>example.com/foo</c> is <c>/bar/nil</c>.</remarks>
|
||||
internal static string PathRelativeToDomain(Uri domainUri, string path)
|
||||
{
|
||||
return path.Substring(domainUri.AbsolutePath.Length).EnsureStartsWith('/');
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ namespace Umbraco.Web.Routing
|
||||
/// </summary>
|
||||
internal interface IContentFinder
|
||||
{
|
||||
/// <summary>
|
||||
/// Tries to find and assign an Umbraco document to a <c>PublishedContentRequest</c>.
|
||||
/// </summary>
|
||||
/// <param name="docRequest">The <c>PublishedContentRequest</c>.</param>
|
||||
/// <returns>A value indicating whether an Umbraco document was found and assigned.</returns>
|
||||
/// <remarks>Optionally, can also assign the template or anything else on the document request, although that is not required.</remarks>
|
||||
bool TryFindDocument(PublishedContentRequest contentRequest);
|
||||
/// <summary>
|
||||
/// Tries to find and assign an Umbraco document to a <c>PublishedContentRequest</c>.
|
||||
/// </summary>
|
||||
/// <param name="contentRequest">The <c>PublishedContentRequest</c>.</param>
|
||||
/// <returns>A value indicating whether an Umbraco document was found and assigned.</returns>
|
||||
/// <remarks>Optionally, can also assign the template or anything else on the document request, although that is not required.</remarks>
|
||||
bool TryFindDocument(PublishedContentRequest contentRequest);
|
||||
}
|
||||
}
|
||||
37
src/Umbraco.Web/Routing/ISiteDomainHelper.cs
Normal file
37
src/Umbraco.Web/Routing/ISiteDomainHelper.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utilities to handle site domains.
|
||||
/// </summary>
|
||||
internal interface ISiteDomainHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Filters a list of <c>DomainAndUri</c> to pick one that best matches the current request.
|
||||
/// </summary>
|
||||
/// <param name="current">The Uri of the current request.</param>
|
||||
/// <param name="domainAndUris">The list of <c>DomainAndUri</c> to filter.</param>
|
||||
/// <returns>The selected <c>DomainAndUri</c>.</returns>
|
||||
/// <remarks>
|
||||
/// <para>If the filter is invoked then <paramref name="domainAndUris"/> is _not_ empty and
|
||||
/// <paramref name="current"/> is _not_ null, and <paramref name="current"/> could not be
|
||||
/// matched with anything in <paramref name="domainAndUris"/>.</para>
|
||||
/// <para>The filter _must_ return something else an exception will be thrown.</para>
|
||||
/// </remarks>
|
||||
DomainAndUri MapDomain(Uri current, DomainAndUri[] domainAndUris);
|
||||
|
||||
/// <summary>
|
||||
/// Filters a list of <c>DomainAndUri</c> to pick those that best matches the current request.
|
||||
/// </summary>
|
||||
/// <param name="current">The Uri of the current request.</param>
|
||||
/// <param name="domainAndUris">The list of <c>DomainAndUri</c> to filter.</param>
|
||||
/// <param name="excludeDefault">A value indicating whether to exclude the current/default domain.</param>
|
||||
/// <returns>The selected <c>DomainAndUri</c> items.</returns>
|
||||
/// <remarks>The filter must return something, even empty, else an exception will be thrown.</remarks>
|
||||
IEnumerable<DomainAndUri> MapDomains(Uri current, DomainAndUri[] domainAndUris, bool excludeDefault);
|
||||
}
|
||||
}
|
||||
41
src/Umbraco.Web/Routing/IUrlProvider.cs
Normal file
41
src/Umbraco.Web/Routing/IUrlProvider.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides urls.
|
||||
/// </summary>
|
||||
internal interface IUrlProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the nice url of a published content.
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext">The Umbraco context.</param>
|
||||
/// <param name="contentCache">The content cache.</param>
|
||||
/// <param name="id">The published content id.</param>
|
||||
/// <param name="current">The current absolute url.</param>
|
||||
/// <param name="absolute">A value indicating whether the url should be absolute in any case.</param>
|
||||
/// <returns>The url for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The url is absolute or relative depending on url indicated by <c>current</c> and settings, unless
|
||||
/// <c>absolute</c> is true, in which case the url is always absolute.</para>
|
||||
/// <para>If the provider is unable to provide a url, it should return <c>null</c>.</para>
|
||||
/// </remarks>
|
||||
string GetUrl(UmbracoContext umbracoContext, IPublishedContentStore contentCache, int id, Uri current, bool absolute);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the other urls of a published content.
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext">The Umbraco context.</param>
|
||||
/// <param name="contentCache">The content cache.</param>
|
||||
/// <param name="id">The published content id.</param>
|
||||
/// <param name="current">The current absolute url.</param>
|
||||
/// <returns>The other urls for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Other urls are those that <c>GetUrl</c> would not return in the current context, but would be valid
|
||||
/// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...).</para>
|
||||
/// </remarks>
|
||||
IEnumerable<string> GetOtherUrls(UmbracoContext umbracoContext, IPublishedContentStore contentCache, int id, Uri current);
|
||||
}
|
||||
}
|
||||
@@ -1,334 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Web.Routing;
|
||||
|
||||
using umbraco;
|
||||
using umbraco.cms.businesslogic.web;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides nice urls for a nodes.
|
||||
/// </summary>
|
||||
internal class NiceUrlProvider
|
||||
{
|
||||
internal const string NullUrl = "#";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NiceUrlProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="publishedContentStore">The content store.</param>
|
||||
/// <param name="umbracoContext">The Umbraco context.</param>
|
||||
public NiceUrlProvider(IPublishedContentStore publishedContentStore, UmbracoContext umbracoContext)
|
||||
{
|
||||
_umbracoContext = umbracoContext;
|
||||
_publishedContentStore = publishedContentStore;
|
||||
this.EnforceAbsoluteUrls = false;
|
||||
}
|
||||
|
||||
private readonly UmbracoContext _umbracoContext;
|
||||
private readonly IPublishedContentStore _publishedContentStore;
|
||||
|
||||
public bool EnforceAbsoluteUrls { get; set; }
|
||||
|
||||
#region GetNiceUrl
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nice url of a node.
|
||||
/// </summary>
|
||||
/// <param name="nodeId">The node identifier.</param>
|
||||
/// <returns>The nice url for the node.</returns>
|
||||
/// <remarks>The url is absolute or relative depending on the current url, settings, and options.</remarks>
|
||||
public string GetNiceUrl(int nodeId)
|
||||
{
|
||||
var absolute = UmbracoSettings.UseDomainPrefixes || this.EnforceAbsoluteUrls;
|
||||
return GetNiceUrl(nodeId, _umbracoContext.CleanedUmbracoUrl, absolute);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nice url of a node.
|
||||
/// </summary>
|
||||
/// <param name="nodeId">The node identifier.</param>
|
||||
/// <param name="absolute">A value indicating whether the url should be absolute in any case.</param>
|
||||
/// <returns>The nice url for the node.</returns>
|
||||
/// <remarks>The url is absolute or relative depending on the current url, unless <c>absolute</c> is true, in which case the url is always absolute.</remarks>
|
||||
public string GetNiceUrl(int nodeId, bool absolute)
|
||||
{
|
||||
return GetNiceUrl(nodeId, _umbracoContext.CleanedUmbracoUrl, absolute);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nice url of a node.
|
||||
/// </summary>
|
||||
/// <param name="nodeId">The node id.</param>
|
||||
/// <param name="current">The current absolute url.</param>
|
||||
/// <param name="absolute">A value indicating whether the url should be absolute in any case.</param>
|
||||
/// <returns>The nice url for the node.</returns>
|
||||
/// <remarks>The url is absolute or relative depending on url indicated by <c>current</c>, unless <c>absolute</c> is true, in which case the url is always absolute.</remarks>
|
||||
public string GetNiceUrl(int nodeId, Uri current, bool absolute)
|
||||
{
|
||||
Uri domainUri;
|
||||
string path;
|
||||
|
||||
if (!current.IsAbsoluteUri)
|
||||
throw new ArgumentException("Current url must be absolute.", "current");
|
||||
|
||||
// do not read cache if previewing
|
||||
var route = _umbracoContext.InPreviewMode
|
||||
? null
|
||||
: _umbracoContext.RoutingContext.RoutesCache.GetRoute(nodeId);
|
||||
|
||||
if (!string.IsNullOrEmpty(route))
|
||||
{
|
||||
// there was a route in the cache - extract domainUri and path
|
||||
// route is /<path> or <domainRootId>/<path>
|
||||
int pos = route.IndexOf('/');
|
||||
path = pos == 0 ? route : route.Substring(pos);
|
||||
domainUri = pos == 0 ? null : DomainUriAtNode(int.Parse(route.Substring(0, pos)), current);
|
||||
}
|
||||
else
|
||||
{
|
||||
// there was no route in the cache - create a route
|
||||
var node = _publishedContentStore.GetDocumentById(_umbracoContext, nodeId);
|
||||
if (node == null)
|
||||
{
|
||||
LogHelper.Warn<NiceUrlProvider>(
|
||||
"Couldn't find any page with nodeId={0}. This is most likely caused by the page not being published.",
|
||||
() => nodeId);
|
||||
|
||||
return NullUrl;
|
||||
}
|
||||
|
||||
// walk up from that node until we hit a node with a domain,
|
||||
// or we reach the content root, collecting urls in the way
|
||||
var pathParts = new List<string>();
|
||||
var n = node;
|
||||
domainUri = DomainUriAtNode(n.Id, current);
|
||||
while (domainUri == null && n != null) // n is null at root
|
||||
{
|
||||
// get the url
|
||||
var urlName = n.UrlName;
|
||||
pathParts.Add(urlName);
|
||||
|
||||
// move to parent node
|
||||
n = n.Parent;
|
||||
domainUri = n == null ? null : DomainUriAtNode(n.Id, current);
|
||||
}
|
||||
|
||||
// no domain, respect HideTopLevelNodeFromPath for legacy purposes
|
||||
if (domainUri == null && global::umbraco.GlobalSettings.HideTopLevelNodeFromPath)
|
||||
ApplyHideTopLevelNodeFromPath(node, pathParts);
|
||||
|
||||
// assemble the route
|
||||
pathParts.Reverse();
|
||||
path = "/" + string.Join("/", pathParts); // will be "/" or "/foo" or "/foo/bar" etc
|
||||
route = (n == null ? "" : n.Id.ToString()) + path;
|
||||
|
||||
// do not store if previewing
|
||||
if (!_umbracoContext.InPreviewMode)
|
||||
_umbracoContext.RoutingContext.RoutesCache.Store(nodeId, route);
|
||||
}
|
||||
|
||||
// assemble the url from domainUri (maybe null) and path
|
||||
return AssembleUrl(domainUri, path, current, absolute).ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetAlternateNiceUrls
|
||||
|
||||
public IEnumerable<string> GetAllAbsoluteNiceUrls(int nodeId)
|
||||
{
|
||||
return GetAllAbsoluteNiceUrls(nodeId, _umbracoContext.CleanedUmbracoUrl);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nice urls of a node.
|
||||
/// </summary>
|
||||
/// <param name="nodeId">The node id.</param>
|
||||
/// <param name="current">The current url.</param>
|
||||
/// <returns>An enumeration of all valid urls for the node.</returns>
|
||||
/// <remarks>The urls are absolute. A node can have more than one url if more than one domain is defined.</remarks>
|
||||
public IEnumerable<string> GetAllAbsoluteNiceUrls(int nodeId, Uri current)
|
||||
{
|
||||
// this is for editContent.aspx which had its own, highly buggy, implementation of NiceUrl...
|
||||
//TODO: finalize & test implementation then replace in editContent.aspx
|
||||
|
||||
string path;
|
||||
IEnumerable<Uri> domainUris;
|
||||
|
||||
// will not read cache if previewing!
|
||||
var route = _umbracoContext.InPreviewMode
|
||||
? null
|
||||
: _umbracoContext.RoutingContext.RoutesCache.GetRoute(nodeId);
|
||||
|
||||
if (!string.IsNullOrEmpty(route))
|
||||
{
|
||||
// there was a route in the cache - extract domainUri and path
|
||||
// route is /<path> or <domainRootId>/<path>
|
||||
int pos = route.IndexOf('/');
|
||||
path = pos == 0 ? route : route.Substring(pos);
|
||||
domainUris = pos == 0 ? new Uri[] { } : DomainUrisAtNode(int.Parse(route.Substring(0, pos)), current);
|
||||
}
|
||||
else
|
||||
{
|
||||
// there was no route in the cache - create a route
|
||||
var node = _publishedContentStore.GetDocumentById(_umbracoContext, nodeId);
|
||||
if (node == null)
|
||||
{
|
||||
LogHelper.Warn<NiceUrlProvider>(
|
||||
"Couldn't find any page with nodeId={0}. This is most likely caused by the page not being published.",
|
||||
() => nodeId);
|
||||
|
||||
return new string[] { NullUrl };
|
||||
}
|
||||
|
||||
// walk up from that node until we hit a node with domains,
|
||||
// or we reach the content root, collecting urls in the way
|
||||
var pathParts = new List<string>();
|
||||
var n = node;
|
||||
domainUris = DomainUrisAtNode(n.Id, current);
|
||||
while (!domainUris.Any() && n != null) // n is null at root
|
||||
{
|
||||
// get the url
|
||||
var urlName = node.UrlName;
|
||||
pathParts.Add(urlName);
|
||||
|
||||
// move to parent node
|
||||
n = n.Parent;
|
||||
domainUris = n == null ? new Uri[] { } : DomainUrisAtNode(n.Id, current);
|
||||
}
|
||||
|
||||
// no domain, respect HideTopLevelNodeFromPath for legacy purposes
|
||||
if (!domainUris.Any() && global::umbraco.GlobalSettings.HideTopLevelNodeFromPath)
|
||||
ApplyHideTopLevelNodeFromPath(node, pathParts);
|
||||
|
||||
// assemble the route
|
||||
pathParts.Reverse();
|
||||
path = "/" + string.Join("/", pathParts); // will be "/" or "/foo" or "/foo/bar" etc
|
||||
route = (n == null ? "" : n.Id.ToString()) + path;
|
||||
|
||||
// do not store if previewing
|
||||
if (!_umbracoContext.InPreviewMode)
|
||||
_umbracoContext.RoutingContext.RoutesCache.Store(nodeId, route);
|
||||
}
|
||||
|
||||
// assemble the alternate urls from domainUris (maybe empty) and path
|
||||
return AssembleUrls(domainUris, path, current).Select(uri => uri.ToString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
Uri AssembleUrl(Uri domainUri, string path, Uri current, bool absolute)
|
||||
{
|
||||
Uri uri;
|
||||
|
||||
if (domainUri == null)
|
||||
{
|
||||
// no domain was found : return an absolute or relative url
|
||||
// ignore vdir at that point
|
||||
if (!absolute || current == null)
|
||||
uri = new Uri(path, UriKind.Relative);
|
||||
else
|
||||
uri = new Uri(current.GetLeftPart(UriPartial.Authority) + path);
|
||||
}
|
||||
else
|
||||
{
|
||||
// a domain was found : return an absolute or relative url
|
||||
// ignore vdir at that point
|
||||
if (!absolute && current != null && domainUri.GetLeftPart(UriPartial.Authority) == current.GetLeftPart(UriPartial.Authority))
|
||||
uri = new Uri(CombinePaths(domainUri.AbsolutePath, path), UriKind.Relative); // relative
|
||||
else
|
||||
uri = new Uri(CombinePaths(domainUri.GetLeftPart(UriPartial.Path), path)); // absolute
|
||||
}
|
||||
|
||||
// UriFromUmbraco will handle vdir
|
||||
// meaning it will add vdir into domain urls too!
|
||||
return UriUtility.UriFromUmbraco(uri);
|
||||
}
|
||||
|
||||
string CombinePaths(string path1, string path2)
|
||||
{
|
||||
string path = path1.TrimEnd('/') + path2;
|
||||
return path == "/" ? path : path.TrimEnd('/');
|
||||
}
|
||||
|
||||
// always build absolute urls unless we really cannot
|
||||
IEnumerable<Uri> AssembleUrls(IEnumerable<Uri> domainUris, string path, Uri current)
|
||||
{
|
||||
List<Uri> uris = new List<Uri>();
|
||||
if (!domainUris.Any())
|
||||
{
|
||||
// no domain was found : return an absolute or relative url
|
||||
// ignore vdir at that point
|
||||
if (current == null)
|
||||
uris.Add(new Uri(path, UriKind.Relative));
|
||||
else
|
||||
uris.Add(new Uri(current.GetLeftPart(UriPartial.Authority) + path));
|
||||
}
|
||||
else
|
||||
{
|
||||
// domains were found : return absolute urls
|
||||
// ignore vdir at that point
|
||||
uris.AddRange(domainUris.Select(domainUri => new Uri(CombinePaths(domainUri.GetLeftPart(UriPartial.Path), path))));
|
||||
}
|
||||
|
||||
// UriFromUmbraco will handle vdir
|
||||
// meaning it will add vdir into domain urls too!
|
||||
return uris.Select(uri => UriUtility.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 = DomainHelper.DomainMatch(Domain.GetDomainsById(nodeId), current, true);
|
||||
return domainAndUri == null ? null : domainAndUri.Uri;
|
||||
}
|
||||
|
||||
IEnumerable<Uri> DomainUrisAtNode(int nodeId, Uri current)
|
||||
{
|
||||
// be safe
|
||||
if (nodeId <= 0)
|
||||
return new Uri[] { };
|
||||
|
||||
var domainAndUris = DomainHelper.DomainMatches(Domain.GetDomainsById(nodeId), current);
|
||||
return domainAndUris.Select(d => d.Uri);
|
||||
}
|
||||
|
||||
void ApplyHideTopLevelNodeFromPath(Core.Models.IPublishedContent node, List<string> pathParts)
|
||||
{
|
||||
// in theory if hideTopLevelNodeFromPath is true, then there should be only once
|
||||
// top-level node, or else domains should be assigned. but for backward compatibility
|
||||
// we add this check - we look for the document matching "/" and if it's not us, then
|
||||
// we do not hide the top level path
|
||||
// it has to be taken care of in IPublishedContentStore.GetDocumentByRoute too so if
|
||||
// "/foo" fails (looking for "/*/foo") we try also "/foo".
|
||||
// this does not make much sense anyway esp. if both "/foo/" and "/bar/foo" exist, but
|
||||
// that's the way it works pre-4.10 and we try to be backward compat for the time being
|
||||
if (node.Parent == null)
|
||||
{
|
||||
var rootNode = _publishedContentStore.GetDocumentByRoute(_umbracoContext, "/", true);
|
||||
if (rootNode.Id == node.Id) // remove only if we're the default node
|
||||
pathParts.RemoveAt(pathParts.Count - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
pathParts.RemoveAt(pathParts.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.Xml;
|
||||
using System.Reflection;
|
||||
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
@@ -47,7 +43,7 @@ namespace Umbraco.Web.Routing
|
||||
// auth. Paul Sterling confirmed in jan. 2013 that we can get rid of it.
|
||||
|
||||
// code from requestHandler.cleanUrl
|
||||
string root = Umbraco.Core.IO.SystemDirectories.Root.ToLower();
|
||||
string root = Core.IO.SystemDirectories.Root.ToLower();
|
||||
if (!string.IsNullOrEmpty(root) && tmp.StartsWith(root))
|
||||
tmp = tmp.Substring(root.Length);
|
||||
tmp = tmp.TrimEnd('/');
|
||||
@@ -81,7 +77,7 @@ namespace Umbraco.Web.Routing
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static IEnumerable<Type> _customHandlerTypes = null;
|
||||
static IEnumerable<Type> _customHandlerTypes;
|
||||
|
||||
static void InitializeNotFoundHandlers()
|
||||
{
|
||||
@@ -93,7 +89,7 @@ namespace Umbraco.Web.Routing
|
||||
var customHandlerTypes = new List<Type>();
|
||||
|
||||
var customHandlers = new XmlDocument();
|
||||
customHandlers.Load(Umbraco.Core.IO.IOHelper.MapPath(Umbraco.Core.IO.SystemFiles.NotFoundhandlersConfig));
|
||||
customHandlers.Load(Core.IO.IOHelper.MapPath(Core.IO.SystemFiles.NotFoundhandlersConfig));
|
||||
|
||||
foreach (XmlNode n in customHandlers.DocumentElement.SelectNodes("notFound"))
|
||||
{
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace Umbraco.Web.Routing
|
||||
LogHelper.Debug<PublishedContentRequestEngine>("{0}Uri=\"{1}\"", () => tracePrefix, () => _pcr.Uri);
|
||||
|
||||
// try to find a domain matching the current request
|
||||
var domainAndUri = DomainHelper.DomainMatch(Domain.GetDomains().ToArray(), _pcr.Uri, false);
|
||||
var domainAndUri = DomainHelper.DomainForUri(DomainHelper.GetAllDomains(false), _pcr.Uri);
|
||||
|
||||
// handle domain
|
||||
if (domainAndUri != null)
|
||||
@@ -216,7 +216,7 @@ namespace Umbraco.Web.Routing
|
||||
var nodePath = _pcr.PublishedContent.Path;
|
||||
LogHelper.Debug<PublishedContentRequestEngine>("{0}Path=\"{1}\"", () => tracePrefix, () => nodePath);
|
||||
var rootNodeId = _pcr.HasDomain ? _pcr.Domain.RootNodeId : (int?)null;
|
||||
var domain = DomainHelper.LookForWildcardDomain(Domain.GetDomains().ToArray(), nodePath, rootNodeId);
|
||||
var domain = DomainHelper.FindWildcardDomainInPath(DomainHelper.GetAllDomains(true), nodePath, rootNodeId);
|
||||
|
||||
if (domain != null)
|
||||
{
|
||||
@@ -270,24 +270,22 @@ namespace Umbraco.Web.Routing
|
||||
// recurse
|
||||
var subdir = directory.GetDirectories(alias.Substring(0, pos)).FirstOrDefault();
|
||||
alias = alias.Substring(pos + 1);
|
||||
return subdir == null ? false : FindTemplateRenderingEngineInDirectory(subdir, alias, extensions);
|
||||
}
|
||||
else
|
||||
{
|
||||
// look here
|
||||
return directory.GetFiles().Any(f => extensions.Any(e => f.Name.InvariantEquals(alias + e)));
|
||||
return subdir != null && FindTemplateRenderingEngineInDirectory(subdir, alias, extensions);
|
||||
}
|
||||
|
||||
// look here
|
||||
return directory.GetFiles().Any(f => extensions.Any(e => f.Name.InvariantEquals(alias + e)));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Document and template
|
||||
|
||||
/// <summary>
|
||||
/// Finds the Umbraco document (if any) matching the request, and updates the PublishedContentRequest accordingly.
|
||||
/// </summary>
|
||||
/// <returns>A value indicating whether a document and template were found.</returns>
|
||||
private bool FindPublishedContentAndTemplate()
|
||||
/// <summary>
|
||||
/// Finds the Umbraco document (if any) matching the request, and updates the PublishedContentRequest accordingly.
|
||||
/// </summary>
|
||||
/// <returns>A value indicating whether a document and template were found.</returns>
|
||||
private void FindPublishedContentAndTemplate()
|
||||
{
|
||||
const string tracePrefix = "FindPublishedContentAndTemplate: ";
|
||||
LogHelper.Debug<PublishedContentRequestEngine>("{0}Path=\"{1}\"", () => tracePrefix, () => _pcr.Uri.AbsolutePath);
|
||||
@@ -299,7 +297,7 @@ namespace Umbraco.Web.Routing
|
||||
// whoever called us is in charge of actually redirecting
|
||||
// -- do not process anything any further --
|
||||
if (_pcr.IsRedirect)
|
||||
return true;
|
||||
return;
|
||||
|
||||
// not handling umbracoRedirect here but after LookupDocument2
|
||||
// so internal redirect, 404, etc has precedence over redirect
|
||||
@@ -315,14 +313,13 @@ namespace Umbraco.Web.Routing
|
||||
|
||||
// handle wildcard domains
|
||||
HandleWildcardDomains();
|
||||
|
||||
return _pcr.HasPublishedContent && _pcr.HasTemplate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the document matching the request, by running the IPublishedContentFinder instances.
|
||||
/// </summary>
|
||||
internal void FindPublishedContent()
|
||||
/// <summary>
|
||||
/// Tries to find the document matching the request, by running the IPublishedContentFinder instances.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">There is no finder collection.</exception>
|
||||
internal void FindPublishedContent()
|
||||
{
|
||||
const string tracePrefix = "FindPublishedContent: ";
|
||||
|
||||
@@ -334,10 +331,12 @@ namespace Umbraco.Web.Routing
|
||||
() => string.Format("{0}Begin finders", tracePrefix),
|
||||
() => string.Format("{0}End finders, {1}", tracePrefix, (_pcr.HasPublishedContent ? "a document was found" : "no document was found"))))
|
||||
{
|
||||
_routingContext.PublishedContentFinders.Any(lookup => lookup.TryFindDocument(_pcr));
|
||||
if (_routingContext.PublishedContentFinders == null)
|
||||
throw new InvalidOperationException("There is no finder collection.");
|
||||
_routingContext.PublishedContentFinders.Any(finder => finder.TryFindDocument(_pcr));
|
||||
}
|
||||
|
||||
// indicate that the published content (if any) we have at the moment is the
|
||||
// indicate that the published content (if any) we have at the moment is the
|
||||
// one that was found by the standard finders before anything else took place.
|
||||
_pcr.SetIsInitialPublishedContent();
|
||||
}
|
||||
@@ -622,7 +621,7 @@ namespace Umbraco.Web.Routing
|
||||
var redirectId = _pcr.PublishedContent.GetPropertyValue("umbracoRedirect", -1);
|
||||
var redirectUrl = "#";
|
||||
if (redirectId > 0)
|
||||
redirectUrl = _routingContext.NiceUrlProvider.GetNiceUrl(redirectId);
|
||||
redirectUrl = _routingContext.UrlProvider.GetUrl(redirectId);
|
||||
if (redirectUrl != "#")
|
||||
_pcr.SetRedirect(redirectUrl);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Umbraco.Web.Routing
|
||||
/// </summary>
|
||||
public IRoutesCache RoutesCache
|
||||
{
|
||||
get { return this.Value; }
|
||||
get { return Value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
@@ -9,28 +8,29 @@ namespace Umbraco.Web.Routing
|
||||
/// </summary>
|
||||
public class RoutingContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext"> </param>
|
||||
/// <param name="contentFinders">The document lookups resolver.</param>
|
||||
/// <param name="contentLastChanceFinder"> </param>
|
||||
/// <param name="publishedContentStore">The content store.</param>
|
||||
/// <param name="niceUrlResolver">The nice urls resolver.</param>
|
||||
internal RoutingContext(
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext"> </param>
|
||||
/// <param name="contentFinders">The document lookups resolver.</param>
|
||||
/// <param name="contentLastChanceFinder"> </param>
|
||||
/// <param name="publishedContentStore">The content store.</param>
|
||||
/// <param name="urlProvider">The nice urls provider.</param>
|
||||
/// <param name="routesCache">The routes cache.</param>
|
||||
internal RoutingContext(
|
||||
UmbracoContext umbracoContext,
|
||||
IEnumerable<IContentFinder> contentFinders,
|
||||
IContentFinder contentLastChanceFinder,
|
||||
IPublishedContentStore publishedContentStore,
|
||||
NiceUrlProvider niceUrlResolver,
|
||||
UrlProvider urlProvider,
|
||||
IRoutesCache routesCache)
|
||||
{
|
||||
this.UmbracoContext = umbracoContext;
|
||||
this.PublishedContentFinders = contentFinders;
|
||||
this.PublishedContentLastChanceFinder = contentLastChanceFinder;
|
||||
this.PublishedContentStore = publishedContentStore;
|
||||
this.NiceUrlProvider = niceUrlResolver;
|
||||
this.RoutesCache = routesCache;
|
||||
UmbracoContext = umbracoContext;
|
||||
PublishedContentFinders = contentFinders;
|
||||
PublishedContentLastChanceFinder = contentLastChanceFinder;
|
||||
PublishedContentStore = publishedContentStore;
|
||||
UrlProvider = urlProvider;
|
||||
RoutesCache = routesCache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -54,9 +54,9 @@ namespace Umbraco.Web.Routing
|
||||
internal IPublishedContentStore PublishedContentStore { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nice urls provider.
|
||||
/// Gets the urls provider.
|
||||
/// </summary>
|
||||
internal NiceUrlProvider NiceUrlProvider { get; private set; }
|
||||
internal UrlProvider UrlProvider { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IRoutesCache"/>
|
||||
|
||||
336
src/Umbraco.Web/Routing/SiteDomainHelper.cs
Normal file
336
src/Umbraco.Web/Routing/SiteDomainHelper.cs
Normal file
@@ -0,0 +1,336 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Text.RegularExpressions;
|
||||
using Umbraco.Core;
|
||||
using umbraco.cms.businesslogic.web;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utilities to handle site domains.
|
||||
/// </summary>
|
||||
internal class SiteDomainHelper : ISiteDomainHelper
|
||||
{
|
||||
#region Configure
|
||||
|
||||
private static readonly ReaderWriterLockSlim ConfigLock = new ReaderWriterLockSlim();
|
||||
private static Dictionary<string, string[]> _sites;
|
||||
private static Dictionary<string, List<string>> _bindings;
|
||||
private static Dictionary<string, Dictionary<string, string[]>> _qualifiedSites;
|
||||
|
||||
// these are for unit tests *only*
|
||||
internal static Dictionary<string, string[]> Sites { get { return _sites; } }
|
||||
internal static Dictionary<string, List<string>> Bindings { get { return _bindings; } }
|
||||
|
||||
// these are for validation
|
||||
//private const string DomainValidationSource = @"^(\*|((?i:http[s]?://)?([-\w]+(\.[-\w]+)*)(:\d+)?(/[-\w]*)?))$";
|
||||
private const string DomainValidationSource = @"^(((?i:http[s]?://)?([-\w]+(\.[-\w]+)*)(:\d+)?(/)?))$";
|
||||
private static readonly Regex DomainValidation = new Regex(DomainValidationSource, RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a disposable object that represents safe write access to config.
|
||||
/// </summary>
|
||||
/// <remarks>Should be used in a <c>using(SiteDomainHelper.ConfigWriteLock) { ... }</c> mode.</remarks>
|
||||
protected static IDisposable ConfigWriteLock
|
||||
{
|
||||
get { return new WriteLock(ConfigLock); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a disposable object that represents safe read access to config.
|
||||
/// </summary>
|
||||
/// <remarks>Should be used in a <c>using(SiteDomainHelper.ConfigWriteLock) { ... }</c> mode.</remarks>
|
||||
protected static IDisposable ConfigReadLock
|
||||
{
|
||||
get { return new ReadLock(ConfigLock); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the entire configuration.
|
||||
/// </summary>
|
||||
public static void Clear()
|
||||
{
|
||||
using (ConfigWriteLock)
|
||||
{
|
||||
_sites = null;
|
||||
_bindings = null;
|
||||
_qualifiedSites = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<string> ValidateDomains(IEnumerable<string> domains)
|
||||
{
|
||||
// must use authority format w/optional scheme and port, but no path
|
||||
// any domain should appear only once
|
||||
return domains.Select(domain =>
|
||||
{
|
||||
if (!DomainValidation.IsMatch(domain))
|
||||
throw new ArgumentOutOfRangeException("domains", string.Format("Invalid domain: \"{0}\"", domain));
|
||||
return domain;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a site.
|
||||
/// </summary>
|
||||
/// <param name="key">A key uniquely identifying the site.</param>
|
||||
/// <param name="domains">The site domains.</param>
|
||||
/// <remarks>At the moment there is no public way to remove a site. Clear and reconfigure.</remarks>
|
||||
public static void AddSite(string key, IEnumerable<string> domains)
|
||||
{
|
||||
using (ConfigWriteLock)
|
||||
{
|
||||
_sites = _sites ?? new Dictionary<string, string[]>();
|
||||
_sites[key] = ValidateDomains(domains).ToArray();
|
||||
_qualifiedSites = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a site.
|
||||
/// </summary>
|
||||
/// <param name="key">A key uniquely identifying the site.</param>
|
||||
/// <param name="domains">The site domains.</param>
|
||||
/// <remarks>At the moment there is no public way to remove a site. Clear and reconfigure.</remarks>
|
||||
public static void AddSite(string key, params string[] domains)
|
||||
{
|
||||
using (ConfigWriteLock)
|
||||
{
|
||||
_sites = _sites ?? new Dictionary<string, string[]>();
|
||||
_sites[key] = ValidateDomains(domains).ToArray();
|
||||
_qualifiedSites = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a site.
|
||||
/// </summary>
|
||||
/// <param name="key">A key uniquely identifying the site.</param>
|
||||
internal static void RemoveSite(string key)
|
||||
{
|
||||
using (ConfigWriteLock)
|
||||
{
|
||||
if (_sites != null && _sites.ContainsKey(key))
|
||||
{
|
||||
_sites.Remove(key);
|
||||
if (_sites.Count == 0)
|
||||
_sites = null;
|
||||
|
||||
if (_bindings != null && _bindings.ContainsKey(key))
|
||||
{
|
||||
foreach (var b in _bindings[key])
|
||||
{
|
||||
_bindings[b].Remove(key);
|
||||
if (_bindings[b].Count == 0)
|
||||
_bindings.Remove(b);
|
||||
}
|
||||
_bindings.Remove(key);
|
||||
if (_bindings.Count > 0)
|
||||
_bindings = null;
|
||||
}
|
||||
|
||||
_qualifiedSites = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds some sites.
|
||||
/// </summary>
|
||||
/// <param name="keys">The keys uniquely identifying the sites to bind.</param>
|
||||
/// <remarks>
|
||||
/// <para>At the moment there is no public way to unbind sites. Clear and reconfigure.</para>
|
||||
/// <para>If site1 is bound to site2 and site2 is bound to site3 then site1 is bound to site3.</para>
|
||||
/// </remarks>
|
||||
public static void BindSites(params string[] keys)
|
||||
{
|
||||
using (ConfigWriteLock)
|
||||
{
|
||||
foreach (var key in keys.Where(key => !_sites.ContainsKey(key)))
|
||||
throw new ArgumentException(string.Format("Not an existing site key: {0}", key), "keys");
|
||||
|
||||
_bindings = _bindings ?? new Dictionary<string, List<string>>();
|
||||
|
||||
var allkeys = _bindings
|
||||
.Where(kvp => keys.Contains(kvp.Key))
|
||||
.SelectMany(kvp => kvp.Value)
|
||||
.Union(keys)
|
||||
.ToArray();
|
||||
|
||||
foreach (var key in allkeys)
|
||||
{
|
||||
if (!_bindings.ContainsKey(key))
|
||||
_bindings[key] = new List<string>();
|
||||
var xkey = key;
|
||||
var addKeys = allkeys.Where(k => k != xkey).Except(_bindings[key]);
|
||||
_bindings[key].AddRange(addKeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Map domains
|
||||
|
||||
/// <summary>
|
||||
/// Filters a list of <c>DomainAndUri</c> to pick one that best matches the current request.
|
||||
/// </summary>
|
||||
/// <param name="current">The Uri of the current request.</param>
|
||||
/// <param name="domainAndUris">The list of <c>DomainAndUri</c> to filter.</param>
|
||||
/// <returns>The selected <c>DomainAndUri</c>.</returns>
|
||||
/// <remarks>
|
||||
/// <para>If the filter is invoked then <paramref name="domainAndUris"/> is _not_ empty and
|
||||
/// <paramref name="current"/> is _not_ null, and <paramref name="current"/> could not be
|
||||
/// matched with anything in <paramref name="domainAndUris"/>.</para>
|
||||
/// <para>The filter _must_ return something else an exception will be thrown.</para>
|
||||
/// </remarks>
|
||||
public virtual DomainAndUri MapDomain(Uri current, DomainAndUri[] domainAndUris)
|
||||
{
|
||||
var currentAuthority = current.GetLeftPart(UriPartial.Authority);
|
||||
var qualifiedSites = GetQualifiedSites(current);
|
||||
|
||||
return MapDomain(domainAndUris, qualifiedSites, currentAuthority);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters a list of <c>DomainAndUri</c> to pick those that best matches the current request.
|
||||
/// </summary>
|
||||
/// <param name="current">The Uri of the current request.</param>
|
||||
/// <param name="domainAndUris">The list of <c>DomainAndUri</c> to filter.</param>
|
||||
/// <param name="excludeDefault">A value indicating whether to exclude the current/default domain.</param>
|
||||
/// <returns>The selected <c>DomainAndUri</c> items.</returns>
|
||||
/// <remarks>The filter must return something, even empty, else an exception will be thrown.</remarks>
|
||||
public virtual IEnumerable<DomainAndUri> MapDomains(Uri current, DomainAndUri[] domainAndUris, bool excludeDefault)
|
||||
{
|
||||
var currentAuthority = current.GetLeftPart(UriPartial.Authority);
|
||||
KeyValuePair<string, string[]>[] candidateSites = null;
|
||||
IEnumerable<DomainAndUri> ret = domainAndUris;
|
||||
|
||||
using (ConfigReadLock) // so nothing changes between GetQualifiedSites and access to bindings
|
||||
{
|
||||
var qualifiedSites = GetQualifiedSitesInsideLock(current);
|
||||
|
||||
if (excludeDefault)
|
||||
{
|
||||
// exclude the current one (avoid producing the absolute equivalent of what GetUrl returns)
|
||||
var hintWithSlash = current.EndPathWithSlash();
|
||||
var hinted = domainAndUris.FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(hintWithSlash));
|
||||
if (hinted != null)
|
||||
ret = ret.Where(d => d != hinted);
|
||||
|
||||
// exclude the default one (avoid producing a possible duplicate of what GetUrl returns)
|
||||
// only if the default one cannot be the current one ie if hinted is not null
|
||||
if (hinted == null && domainAndUris.Any())
|
||||
{
|
||||
// it is illegal to call MapDomain if domainAndUris is empty
|
||||
// also, domainAndUris should NOT contain current, hence the test on hinted
|
||||
var mainDomain = MapDomain(domainAndUris, qualifiedSites, currentAuthority); // what GetUrl would get
|
||||
ret = ret.Where(d => d != mainDomain);
|
||||
}
|
||||
}
|
||||
|
||||
// we do our best, but can't do the impossible
|
||||
if (qualifiedSites == null)
|
||||
return ret;
|
||||
|
||||
// find a site that contains the current authority
|
||||
var currentSite = qualifiedSites.FirstOrDefault(site => site.Value.Contains(currentAuthority));
|
||||
|
||||
// if current belongs to a site, pick every element from domainAndUris that also belong
|
||||
// to that site -- or to any site bound to that site
|
||||
|
||||
if (!currentSite.Equals(default(KeyValuePair<string, string[]>)))
|
||||
{
|
||||
candidateSites = new[] { currentSite };
|
||||
if (_bindings != null && _bindings.ContainsKey(currentSite.Key))
|
||||
{
|
||||
var boundSites = qualifiedSites.Where(site => _bindings[currentSite.Key].Contains(site.Key));
|
||||
candidateSites = candidateSites.Union(boundSites).ToArray();
|
||||
|
||||
// .ToArray ensures it is evaluated before the configuration lock is exited
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we are able to filter, then filter, else return the whole lot
|
||||
return candidateSites == null ? ret : ret.Where(d =>
|
||||
{
|
||||
var authority = d.Uri.GetLeftPart(UriPartial.Authority);
|
||||
return candidateSites.Any(site => site.Value.Contains(authority));
|
||||
});
|
||||
}
|
||||
|
||||
private static Dictionary<string, string[]> GetQualifiedSites(Uri current)
|
||||
{
|
||||
using (ConfigReadLock)
|
||||
{
|
||||
return GetQualifiedSitesInsideLock(current);
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, string[]> GetQualifiedSitesInsideLock(Uri current)
|
||||
{
|
||||
// we do our best, but can't do the impossible
|
||||
if (_sites == null)
|
||||
return null;
|
||||
|
||||
// cached?
|
||||
if (_qualifiedSites != null && _qualifiedSites.ContainsKey(current.Scheme))
|
||||
return _qualifiedSites[current.Scheme];
|
||||
|
||||
_qualifiedSites = _qualifiedSites ?? new Dictionary<string, Dictionary<string, string[]>>();
|
||||
|
||||
// convert sites into authority sites based upon current scheme
|
||||
// because some domains in the sites might not have a scheme -- and cache
|
||||
return _qualifiedSites[current.Scheme] = _sites
|
||||
.ToDictionary(
|
||||
kvp => kvp.Key,
|
||||
kvp => kvp.Value.Select(d => new Uri(UriUtility.StartWithScheme(d, current.Scheme)).GetLeftPart(UriPartial.Authority)).ToArray()
|
||||
);
|
||||
|
||||
// .ToDictionary will evaluate and create the dictionary immediately
|
||||
// the new value is .ToArray so it will also be evaluated immediately
|
||||
// therefore it is safe to return and exit the configuration lock
|
||||
}
|
||||
|
||||
private static DomainAndUri MapDomain(DomainAndUri[] domainAndUris, Dictionary<string, string[]> qualifiedSites, string currentAuthority)
|
||||
{
|
||||
if (domainAndUris == null)
|
||||
throw new ArgumentNullException("domainAndUris");
|
||||
if (!domainAndUris.Any())
|
||||
throw new ArgumentException("Cannot be empty.", "domainAndUris");
|
||||
|
||||
// we do our best, but can't do the impossible
|
||||
if (qualifiedSites == null)
|
||||
return domainAndUris.First();
|
||||
|
||||
// find a site that contains the current authority
|
||||
var currentSite = qualifiedSites.FirstOrDefault(site => site.Value.Contains(currentAuthority));
|
||||
|
||||
// if current belongs to a site - try to pick the first element
|
||||
// from domainAndUris that also belongs to that site
|
||||
var ret = currentSite.Equals(default(KeyValuePair<string, string[]>))
|
||||
? null
|
||||
: domainAndUris.FirstOrDefault(d => currentSite.Value.Contains(d.Uri.GetLeftPart(UriPartial.Authority)));
|
||||
|
||||
// no match means that either current does not belong to a site, or the site it belongs to
|
||||
// does not contain any of domainAndUris. Yet we have to return something. here, it becomes
|
||||
// a bit arbitrary.
|
||||
|
||||
// look through sites in order and pick the first domainAndUri that belongs to a site
|
||||
ret = ret ?? qualifiedSites
|
||||
.Where(site => site.Key != currentSite.Key)
|
||||
.Select(site => domainAndUris.FirstOrDefault(domainAndUri => site.Value.Contains(domainAndUri.Uri.GetLeftPart(UriPartial.Authority))))
|
||||
.FirstOrDefault(domainAndUri => domainAndUri != null);
|
||||
|
||||
// random, really
|
||||
ret = ret ?? domainAndUris.First();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
38
src/Umbraco.Web/Routing/SiteDomainHelperResolver.cs
Normal file
38
src/Umbraco.Web/Routing/SiteDomainHelperResolver.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolves the <see cref="ISiteDomainHelper"/> implementation.
|
||||
/// </summary>
|
||||
internal sealed class SiteDomainHelperResolver : SingleObjectResolverBase<SiteDomainHelperResolver, ISiteDomainHelper>
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SiteDomainHelperResolver"/> class with an <see cref="ISiteDomainHelper"/> implementation.
|
||||
/// </summary>
|
||||
/// <param name="helper">The <see cref="ISiteDomainHelper"/> implementation.</param>
|
||||
internal SiteDomainHelperResolver(ISiteDomainHelper helper)
|
||||
: base(helper)
|
||||
{ }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Can be used by developers at runtime to set their IDomainHelper at app startup
|
||||
/// </summary>
|
||||
/// <param name="helper"></param>
|
||||
public void SetHelper(ISiteDomainHelper helper)
|
||||
{
|
||||
Value = helper;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ISiteDomainHelper"/> implementation.
|
||||
/// </summary>
|
||||
public ISiteDomainHelper Helper
|
||||
{
|
||||
get { return Value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
133
src/Umbraco.Web/Routing/UrlProvider.cs
Normal file
133
src/Umbraco.Web/Routing/UrlProvider.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides urls.
|
||||
/// </summary>
|
||||
internal class UrlProvider
|
||||
{
|
||||
#region Ctor and configuration
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UrlProvider"/> class with an Umbraco context, a content cache, and a list of url providers.
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext">The Umbraco context.</param>
|
||||
/// <param name="contentCache">The content cache.</param>
|
||||
/// <param name="urlProviders">The list of url providers.</param>
|
||||
public UrlProvider(UmbracoContext umbracoContext, IPublishedContentStore contentCache,
|
||||
IEnumerable<IUrlProvider> urlProviders)
|
||||
{
|
||||
_umbracoContext = umbracoContext;
|
||||
_contentCache = contentCache;
|
||||
_urlProviders = urlProviders;
|
||||
EnforceAbsoluteUrls = false;
|
||||
}
|
||||
|
||||
private readonly UmbracoContext _umbracoContext;
|
||||
private readonly IPublishedContentStore _contentCache;
|
||||
private readonly IEnumerable<IUrlProvider> _urlProviders;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the provider should enforce absolute urls.
|
||||
/// </summary>
|
||||
public bool EnforceAbsoluteUrls { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetUrl
|
||||
|
||||
/// <summary>
|
||||
/// Gets the url of a published content.
|
||||
/// </summary>
|
||||
/// <param name="id">The published content identifier.</param>
|
||||
/// <returns>The url for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The url is absolute or relative depending on the current url, settings, and options.</para>
|
||||
/// <para>If the provider is unable to provide a url, it returns "#".</para>
|
||||
/// </remarks>
|
||||
public string GetUrl(int id)
|
||||
{
|
||||
var absolute = UmbracoSettings.UseDomainPrefixes | EnforceAbsoluteUrls;
|
||||
return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, absolute);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nice url of a published content.
|
||||
/// </summary>
|
||||
/// <param name="id">The published content identifier.</param>
|
||||
/// <param name="absolute">A value indicating whether the url should be absolute in any case.</param>
|
||||
/// <returns>The url for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The url is absolute or relative depending on the current url and settings, unless <c>absolute</c> is true,
|
||||
/// in which case the url is always absolute.</para>
|
||||
/// <para>If the provider is unable to provide a url, it returns "#".</para>
|
||||
/// </remarks>
|
||||
public string GetUrl(int id, bool absolute)
|
||||
{
|
||||
absolute = absolute | EnforceAbsoluteUrls;
|
||||
return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, absolute);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nice url of a published content.
|
||||
/// </summary>
|
||||
/// <param name="id">The published content id.</param>
|
||||
/// <param name="current">The current absolute url.</param>
|
||||
/// <param name="absolute">A value indicating whether the url should be absolute in any case.</param>
|
||||
/// <returns>The url for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The url is absolute or relative depending on url indicated by <c>current</c> and settings, unless
|
||||
/// <c>absolute</c> is true, in which case the url is always absolute.</para>
|
||||
/// <para>If the provider is unable to provide a url, it returns "#".</para>
|
||||
/// </remarks>
|
||||
public string GetUrl(int id, Uri current, bool absolute)
|
||||
{
|
||||
absolute = absolute | EnforceAbsoluteUrls;
|
||||
var url = _urlProviders.Select(provider => provider.GetUrl(_umbracoContext, _contentCache, id, current, absolute)).FirstOrDefault(u => u != null);
|
||||
return url ?? "#"; // legacy wants this
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetOtherUrls
|
||||
|
||||
/// <summary>
|
||||
/// Gets the other urls of a published content.
|
||||
/// </summary>
|
||||
/// <param name="id">The published content id.</param>
|
||||
/// <returns>The other urls for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Other urls are those that <c>GetUrl</c> would not return in the current context, but would be valid
|
||||
/// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...).</para>
|
||||
/// <para>The results depend on the current url.</para>
|
||||
/// </remarks>
|
||||
public IEnumerable<string> GetOtherUrls(int id)
|
||||
{
|
||||
return GetOtherUrls(id, _umbracoContext.CleanedUmbracoUrl);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the other urls of a published content.
|
||||
/// </summary>
|
||||
/// <param name="id">The published content id.</param>
|
||||
/// <param name="current">The current absolute url.</param>
|
||||
/// <returns>The other urls for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Other urls are those that <c>GetUrl</c> would not return in the current context, but would be valid
|
||||
/// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...).</para>
|
||||
/// </remarks>
|
||||
public IEnumerable<string> GetOtherUrls(int id, Uri current)
|
||||
{
|
||||
// providers can return null or an empty list or a non-empty list, be prepared
|
||||
var urls = _urlProviders.SelectMany(provider => provider.GetOtherUrls(_umbracoContext, _contentCache, id, current) ?? Enumerable.Empty<string>());
|
||||
|
||||
return urls;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
41
src/Umbraco.Web/Routing/UrlProviderResolver.cs
Normal file
41
src/Umbraco.Web/Routing/UrlProviderResolver.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolves IUrlProvider objects.
|
||||
/// </summary>
|
||||
internal sealed class UrlProviderResolver : ManyObjectsResolverBase<UrlProviderResolver, IUrlProvider>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UrlProviderResolver"/> class with an initial list of provider types.
|
||||
/// </summary>
|
||||
/// <param name="providerTypes">The list of provider types.</param>
|
||||
/// <remarks>The resolver is created by the <c>WebBootManager</c> and thus the constructor remains internal.</remarks>
|
||||
internal UrlProviderResolver(IEnumerable<Type> providerTypes)
|
||||
: base(providerTypes)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UrlProviderResolver"/> class with an initial list of provider types.
|
||||
/// </summary>
|
||||
/// <param name="providerTypes">The list of provider types.</param>
|
||||
/// <remarks>The resolver is created by the <c>WebBootManager</c> and thus the constructor remains internal.</remarks>
|
||||
internal UrlProviderResolver(params Type[] providerTypes)
|
||||
: base(providerTypes)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the providers.
|
||||
/// </summary>
|
||||
public IEnumerable<IUrlProvider> Providers
|
||||
{
|
||||
get { return Values; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ namespace Umbraco.Web.Templates
|
||||
return text;
|
||||
}
|
||||
|
||||
var niceUrlsProvider = UmbracoContext.Current.NiceUrlProvider;
|
||||
var urlProvider = UmbracoContext.Current.UrlProvider;
|
||||
|
||||
// Parse internal links
|
||||
MatchCollection tags = Regex.Matches(text, @"href=""[/]?(?:\{|\%7B)localLink:([0-9]+)(?:\}|\%7D)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
||||
@@ -36,7 +36,7 @@ namespace Umbraco.Web.Templates
|
||||
if (tag.Groups.Count > 0)
|
||||
{
|
||||
string id = tag.Groups[1].Value; //.Remove(tag.Groups[1].Value.Length - 1, 1);
|
||||
string newLink = niceUrlsProvider.GetNiceUrl(int.Parse(id));
|
||||
string newLink = urlProvider.GetUrl(int.Parse(id));
|
||||
text = text.Replace(tag.Value.ToString(), "href=\"" + newLink);
|
||||
}
|
||||
return text;
|
||||
|
||||
@@ -326,6 +326,13 @@
|
||||
<Compile Include="Mvc\SurfaceRouteHandler.cs" />
|
||||
<Compile Include="Search\ExamineIndexerModel.cs" />
|
||||
<Compile Include="Search\LuceneIndexerExtensions.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\AssignDomain2.aspx.cs">
|
||||
<DependentUpon>AssignDomain2.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\AssignDomain2.aspx.designer.cs">
|
||||
<DependentUpon>AssignDomain2.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="UrlHelperExtensions.cs" />
|
||||
<Compile Include="WebApi\MemberAuthorizeAttribute.cs" />
|
||||
<Compile Include="WebApi\UmbracoApiController.cs" />
|
||||
@@ -379,15 +386,24 @@
|
||||
<Compile Include="RenderFieldCaseType.cs" />
|
||||
<Compile Include="RenderFieldEncodingType.cs" />
|
||||
<Compile Include="RouteCollectionExtensions.cs" />
|
||||
<Compile Include="Routing\AliasUrlProvider.cs" />
|
||||
<Compile Include="Routing\ContentFinderByLegacy404.cs" />
|
||||
<Compile Include="Routing\ContentFinderByNotFoundHandler.cs" />
|
||||
<Compile Include="Routing\ContentFinderByPageIdQuery.cs" />
|
||||
<Compile Include="Mvc\SurfaceControllerResolver.cs" />
|
||||
<Compile Include="Routing\ContentFinderByNotFoundHandlers.cs" />
|
||||
<Compile Include="Routing\ISiteDomainHelper.cs" />
|
||||
<Compile Include="Routing\RoutableAttemptEventArgs.cs" />
|
||||
<Compile Include="Routing\DefaultUrlProvider.cs" />
|
||||
<Compile Include="Routing\DomainAndUri.cs" />
|
||||
<Compile Include="Routing\IUrlProvider.cs" />
|
||||
<Compile Include="Routing\SiteDomainHelper.cs" />
|
||||
<Compile Include="Routing\SiteDomainHelperResolver.cs" />
|
||||
<Compile Include="Routing\UrlProviderResolver.cs" />
|
||||
<Compile Include="Routing\NotFoundHandlerHelper.cs" />
|
||||
<Compile Include="Routing\EnsureRoutableOutcome.cs" />
|
||||
<Compile Include="Routing\PublishedContentRequestEngine.cs" />
|
||||
<Compile Include="Routing\UrlProvider.cs" />
|
||||
<Compile Include="Routing\WebServicesRouteConstraint.cs" />
|
||||
<Compile Include="Search\ExamineSearcherModel.cs" />
|
||||
<Compile Include="Search\ExamineEvents.cs" />
|
||||
@@ -569,7 +585,6 @@
|
||||
<Compile Include="Routing\ContentFinderByNiceUrl.cs" />
|
||||
<Compile Include="Routing\ContentFinderByNiceUrlAndTemplate.cs" />
|
||||
<Compile Include="Routing\ContentFinderByProfile.cs" />
|
||||
<Compile Include="Routing\NiceUrlProvider.cs" />
|
||||
<Compile Include="PluginManagerExtensions.cs" />
|
||||
<Compile Include="Routing\DomainHelper.cs" />
|
||||
<Compile Include="Routing\ContentFinderResolver.cs" />
|
||||
@@ -1763,6 +1778,7 @@
|
||||
</Compile>
|
||||
<Compile Include="WebServices\BulkPublishController.cs" />
|
||||
<Compile Include="WebServices\CoreStringsController.cs" />
|
||||
<Compile Include="WebServices\DomainsApiController.cs" />
|
||||
<Compile Include="WebServices\ExamineManagementApiController.cs" />
|
||||
<Compile Include="WebServices\FolderBrowserService.cs" />
|
||||
<Compile Include="WebServices\SaveFileController.cs" />
|
||||
@@ -1786,6 +1802,9 @@
|
||||
<Content Include="umbraco.presentation\umbraco\dashboard\StartupDashboardIntro.ascx" />
|
||||
<Content Include="umbraco.presentation\umbraco\dashboard\StartupDashboardKits.ascx" />
|
||||
<Content Include="umbraco.presentation\umbraco\dashboard\StartupDashboardVideos.ascx" />
|
||||
<Content Include="umbraco.presentation\umbraco\dialogs\AssignDomain2.aspx">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Content>
|
||||
<Content Include="umbraco.presentation\umbraco\dialogs\TemplateSkinning.aspx" />
|
||||
<Content Include="umbraco.presentation\umbraco\LiveEditing\Modules\SkinModule\CssParser.aspx" />
|
||||
<Content Include="umbraco.presentation\umbraco\LiveEditing\Modules\SkinModule\ImageUploader.aspx" />
|
||||
|
||||
@@ -84,7 +84,11 @@ namespace Umbraco.Web
|
||||
var umbracoContext = new UmbracoContext(httpContext, applicationContext);
|
||||
|
||||
// create the nice urls provider
|
||||
var niceUrls = new NiceUrlProvider(PublishedContentStoreResolver.Current.PublishedContentStore, umbracoContext);
|
||||
// there's one per request because there are some behavior parameters that can be changed
|
||||
var urlProvider = new UrlProvider(
|
||||
umbracoContext,
|
||||
PublishedContentStoreResolver.Current.PublishedContentStore,
|
||||
UrlProviderResolver.Current.Providers);
|
||||
|
||||
// create the RoutingContext, and assign
|
||||
var routingContext = new RoutingContext(
|
||||
@@ -92,7 +96,7 @@ namespace Umbraco.Web
|
||||
ContentFinderResolver.Current.Finders,
|
||||
ContentLastChanceFinderResolver.Current.Finder,
|
||||
PublishedContentStoreResolver.Current.PublishedContentStore,
|
||||
niceUrls,
|
||||
urlProvider,
|
||||
RoutesCacheResolver.Current.RoutesCache);
|
||||
|
||||
//assign the routing context back
|
||||
@@ -270,13 +274,13 @@ namespace Umbraco.Web
|
||||
/// <remarks>
|
||||
/// If the RoutingContext is null, this will throw an exception.
|
||||
/// </remarks>
|
||||
internal NiceUrlProvider NiceUrlProvider
|
||||
internal UrlProvider UrlProvider
|
||||
{
|
||||
get
|
||||
{
|
||||
if (RoutingContext == null)
|
||||
throw new InvalidOperationException("Cannot access the NiceUrlProvider when the UmbracoContext's RoutingContext is null");
|
||||
return RoutingContext.NiceUrlProvider;
|
||||
throw new InvalidOperationException("Cannot access the UrlProvider when the UmbracoContext's RoutingContext is null");
|
||||
return RoutingContext.UrlProvider;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -416,8 +416,8 @@ namespace Umbraco.Web
|
||||
/// <returns>String with a friendly url from a node</returns>
|
||||
public string NiceUrl(int nodeId)
|
||||
{
|
||||
var niceUrlsProvider = UmbracoContext.Current.NiceUrlProvider;
|
||||
return niceUrlsProvider.GetNiceUrl(nodeId);
|
||||
var urlProvider = UmbracoContext.Current.UrlProvider;
|
||||
return urlProvider.GetUrl(nodeId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -427,8 +427,8 @@ namespace Umbraco.Web
|
||||
/// <returns>String with a friendly url with full domain from a node</returns>
|
||||
public string NiceUrlWithDomain(int nodeId)
|
||||
{
|
||||
var niceUrlsProvider = UmbracoContext.Current.NiceUrlProvider;
|
||||
return niceUrlsProvider.GetNiceUrl(nodeId, true);
|
||||
var urlProvider = UmbracoContext.Current.UrlProvider;
|
||||
return urlProvider.GetUrl(nodeId, true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Compilation;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using System.Web.UI;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Web.Routing;
|
||||
@@ -205,7 +199,7 @@ namespace Umbraco.Web
|
||||
{
|
||||
var uri = context.OriginalRequestUrl;
|
||||
|
||||
var reason = EnsureRoutableOutcome.IsRoutable;;
|
||||
var reason = EnsureRoutableOutcome.IsRoutable;
|
||||
|
||||
// ensure this is a document request
|
||||
if (!EnsureDocumentRequest(httpContext, uri))
|
||||
@@ -244,9 +238,9 @@ namespace Umbraco.Web
|
||||
|
||||
// handle directory-urls used for asmx
|
||||
// legacy - what's the point really?
|
||||
if (maybeDoc && GlobalSettings.UseDirectoryUrls)
|
||||
if (/*maybeDoc &&*/ GlobalSettings.UseDirectoryUrls)
|
||||
{
|
||||
int asmxPos = lpath.IndexOf(".asmx/");
|
||||
int asmxPos = lpath.IndexOf(".asmx/", StringComparison.OrdinalIgnoreCase);
|
||||
if (asmxPos >= 0)
|
||||
{
|
||||
// use uri.AbsolutePath, not path, 'cos path has been lowercased
|
||||
@@ -320,41 +314,36 @@ namespace Umbraco.Web
|
||||
// ensures Umbraco has at least one published node
|
||||
// if not, rewrites to splash and return false
|
||||
// if yes, return true
|
||||
bool EnsureHasContent(UmbracoContext context, HttpContextBase httpContext)
|
||||
private static bool EnsureHasContent(UmbracoContext context, HttpContextBase httpContext)
|
||||
{
|
||||
var store = context.RoutingContext.PublishedContentStore;
|
||||
if (!store.HasContent(context))
|
||||
{
|
||||
LogHelper.Warn<UmbracoModule>("Umbraco has no content");
|
||||
if (store.HasContent(context))
|
||||
return true;
|
||||
|
||||
httpContext.Response.StatusCode = 503;
|
||||
LogHelper.Warn<UmbracoModule>("Umbraco has no content");
|
||||
|
||||
var noContentUrl = "~/config/splashes/noNodes.aspx";
|
||||
httpContext.RewritePath(UriUtility.ToAbsolute(noContentUrl));
|
||||
httpContext.Response.StatusCode = 503;
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
const string noContentUrl = "~/config/splashes/noNodes.aspx";
|
||||
httpContext.RewritePath(UriUtility.ToAbsolute(noContentUrl));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ensures Umbraco is configured
|
||||
// if not, redirect to install and return false
|
||||
// if yes, return true
|
||||
bool EnsureIsConfigured(HttpContextBase httpContext, Uri uri)
|
||||
{
|
||||
if (!ApplicationContext.Current.IsConfigured)
|
||||
{
|
||||
LogHelper.Warn<UmbracoModule>("Umbraco is not configured");
|
||||
private static bool EnsureIsConfigured(HttpContextBase httpContext, Uri uri)
|
||||
{
|
||||
if (ApplicationContext.Current.IsConfigured)
|
||||
return true;
|
||||
|
||||
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;
|
||||
}
|
||||
return true;
|
||||
LogHelper.Warn<UmbracoModule>("Umbraco is not configured");
|
||||
|
||||
var installPath = UriUtility.ToAbsolute(Core.IO.SystemDirectories.Install);
|
||||
var installUrl = string.Format("{0}/default.aspx?redir=true&url={1}", installPath, HttpUtility.UrlEncode(uri.ToString()));
|
||||
httpContext.Response.Redirect(installUrl, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -433,7 +422,7 @@ namespace Umbraco.Web
|
||||
BeginRequest(new HttpContextWrapper(httpContext));
|
||||
};
|
||||
|
||||
app.PostResolveRequestCache += (sender, e) =>
|
||||
app.PostResolveRequestCache += (sender, e) =>
|
||||
{
|
||||
var httpContext = ((HttpApplication)sender).Context;
|
||||
ProcessRequest(new HttpContextWrapper(httpContext));
|
||||
|
||||
@@ -275,22 +275,25 @@ namespace Umbraco.Web
|
||||
PublishedMediaStoreResolver.Current = new PublishedMediaStoreResolver(new DefaultPublishedMediaStore());
|
||||
|
||||
FilteredControllerFactoriesResolver.Current = new FilteredControllerFactoriesResolver(
|
||||
//add all known factories, devs can then modify this list on application startup either by binding to events
|
||||
//or in their own global.asax
|
||||
// add all known factories, devs can then modify this list on application
|
||||
// startup either by binding to events or in their own global.asax
|
||||
new[]
|
||||
{
|
||||
typeof (RenderControllerFactory)
|
||||
});
|
||||
|
||||
UrlProviderResolver.Current = new UrlProviderResolver(
|
||||
//typeof(AliasUrlProvider), // not enabled by default
|
||||
typeof(DefaultUrlProvider)
|
||||
);
|
||||
|
||||
// the legacy 404 will run from within ContentFinderByNotFoundHandlers below
|
||||
// so for the time being there is no last chance finder
|
||||
ContentLastChanceFinderResolver.Current = new ContentLastChanceFinderResolver();
|
||||
|
||||
ContentFinderResolver.Current = new ContentFinderResolver(
|
||||
//add all known resolvers in the correct order, devs can then modify this list on application startup either by binding to events
|
||||
//or in their own global.asax
|
||||
new[]
|
||||
{
|
||||
// add all known resolvers in the correct order, devs can then modify this list
|
||||
// on application startup either by binding to events or in their own global.asax
|
||||
typeof (ContentFinderByPageIdQuery),
|
||||
typeof (ContentFinderByNiceUrl),
|
||||
typeof (ContentFinderByIdPath),
|
||||
@@ -300,7 +303,9 @@ namespace Umbraco.Web
|
||||
//typeof (ContentFinderByProfile),
|
||||
//typeof (ContentFinderByUrlAlias),
|
||||
typeof (ContentFinderByNotFoundHandlers)
|
||||
});
|
||||
);
|
||||
|
||||
SiteDomainHelperResolver.Current = new SiteDomainHelperResolver(new SiteDomainHelper());
|
||||
|
||||
RoutesCacheResolver.Current = new RoutesCacheResolver(new DefaultRoutesCache(_isForTesting == false));
|
||||
|
||||
|
||||
120
src/Umbraco.Web/WebServices/DomainsApiController.cs
Normal file
120
src/Umbraco.Web/WebServices/DomainsApiController.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Web.Http;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Web.WebApi;
|
||||
//using umbraco.cms.businesslogic.language;
|
||||
using umbraco.cms.businesslogic.web;
|
||||
|
||||
namespace Umbraco.Web.WebServices
|
||||
{
|
||||
/// <summary>
|
||||
/// A REST controller used for managing domains.
|
||||
/// </summary>
|
||||
/// <remarks>Nothing to do with Active Directory.</remarks>
|
||||
public class DomainsApiController : UmbracoAuthorizedApiController
|
||||
{
|
||||
[HttpPost]
|
||||
// can't pass multiple complex args in json post request...
|
||||
public PostBackModel SaveLanguageAndDomains(PostBackModel model)
|
||||
{
|
||||
var node = ApplicationContext.Current.Services.ContentService.GetById(model.NodeId);
|
||||
|
||||
if (node == null)
|
||||
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.BadRequest)
|
||||
{
|
||||
Content = new StringContent(string.Format("There is no content node with id {0}.", model.NodeId)),
|
||||
ReasonPhrase = "Node Not Found."
|
||||
});
|
||||
|
||||
if (!UmbracoUser.GetPermissions(node.Path).Contains(global::umbraco.BusinessLogic.Actions.ActionAssignDomain.Instance.Letter))
|
||||
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized)
|
||||
{
|
||||
Content = new StringContent("You do not have permission to assign domains on that node."),
|
||||
ReasonPhrase = "Permission Denied."
|
||||
});
|
||||
|
||||
model.Valid = true;
|
||||
var domains = Routing.DomainHelper.GetNodeDomains(model.NodeId, true);
|
||||
var languages = global::umbraco.cms.businesslogic.language.Language.GetAllAsList().ToArray();
|
||||
var language = model.Language > 0 ? languages.FirstOrDefault(l => l.id == model.Language) : null;
|
||||
|
||||
// process wildcard
|
||||
|
||||
if (language != null)
|
||||
{
|
||||
var wildcard = domains.FirstOrDefault(d => d.IsWildcard);
|
||||
if (wildcard != null)
|
||||
wildcard.Language = language;
|
||||
else // yet there is a race condition here...
|
||||
Domain.MakeNew("*" + model.NodeId, model.NodeId, model.Language);
|
||||
}
|
||||
else
|
||||
{
|
||||
var wildcard = domains.FirstOrDefault(d => d.IsWildcard);
|
||||
if (wildcard != null)
|
||||
wildcard.Delete();
|
||||
}
|
||||
|
||||
// process domains
|
||||
|
||||
foreach (var domain in domains.Where(d => model.Domains.All(m => !m.Name.Equals(d.Name, StringComparison.OrdinalIgnoreCase))))
|
||||
domain.Delete();
|
||||
|
||||
var names = new List<string>();
|
||||
|
||||
foreach (var domainModel in model.Domains.Where(m => !string.IsNullOrWhiteSpace(m.Name)))
|
||||
{
|
||||
language = languages.FirstOrDefault(l => l.id == domainModel.Lang);
|
||||
if (language == null)
|
||||
continue;
|
||||
var name = domainModel.Name.ToLowerInvariant();
|
||||
if (names.Contains(name))
|
||||
{
|
||||
domainModel.Duplicate = true;
|
||||
continue;
|
||||
}
|
||||
names.Add(name);
|
||||
var domain = domains.FirstOrDefault(d => d.Name.Equals(domainModel.Name, StringComparison.OrdinalIgnoreCase));
|
||||
if (domain != null)
|
||||
domain.Language = language;
|
||||
else if (Domain.Exists(domainModel.Name))
|
||||
domainModel.Duplicate = true;
|
||||
else // yet there is a race condition here...
|
||||
Domain.MakeNew(name, model.NodeId, domainModel.Lang);
|
||||
}
|
||||
|
||||
model.Valid = model.Domains.All(m => !m.Duplicate);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
#region Models
|
||||
|
||||
public class PostBackModel
|
||||
{
|
||||
public bool Valid { get; set; }
|
||||
public int NodeId { get; set; }
|
||||
public int Language { get; set; }
|
||||
public DomainModel[] Domains { get; set; }
|
||||
}
|
||||
|
||||
public class DomainModel
|
||||
{
|
||||
public DomainModel(string name, int lang)
|
||||
{
|
||||
Name = name;
|
||||
Lang = lang;
|
||||
}
|
||||
|
||||
public string Name { get; private set; }
|
||||
public int Lang { get; private set; }
|
||||
public bool Duplicate { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<%@ Page Language="c#" MasterPageFile="../masterpages/umbracoDialog.Master" Codebehind="AssignDomain2.aspx.cs" AutoEventWireup="True" Inherits="umbraco.dialogs.AssignDomain2" %>
|
||||
<%@ Import Namespace="Umbraco.Web" %>
|
||||
<%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %>
|
||||
<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %>
|
||||
|
||||
<asp:Content ContentPlaceHolderID="head" runat="server">
|
||||
<umb:JsInclude runat="server" FilePath="Dialogs/AssignDomain2.js" PathNameAlias="UmbracoClient" />
|
||||
<umb:JsInclude runat="server" FilePath="Application/JQuery/jquery.validate.min.js" PathNameAlias="UmbracoClient" />
|
||||
<umb:CssInclude runat="server" FilePath="Dialogs/AssignDomain2.css" PathNameAlias="UmbracoClient" />
|
||||
<script type="text/javascript">
|
||||
|
||||
(function ($) {
|
||||
$(document).ready(function () {
|
||||
var dialog = new Umbraco.Dialogs.AssignDomain2({
|
||||
nodeId: <%=GetNodeId()%>,
|
||||
restServiceLocation: '<%=GetRestServicePath() %>',
|
||||
invalidDomain: '<%=umbraco.ui.Text("assignDomain", "invalidDomain") %>',
|
||||
duplicateDomain: '<%=umbraco.ui.Text("assignDomain", "duplicateDomain") %>',
|
||||
<asp:Literal runat="server" ID="data" />
|
||||
});
|
||||
dialog.init();
|
||||
});
|
||||
})(jQuery);
|
||||
|
||||
</script>
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ContentPlaceHolderID="body" runat="server">
|
||||
|
||||
<cc1:Feedback ID="feedback" runat="server" />
|
||||
|
||||
<div id="komask"></div>
|
||||
<div>
|
||||
<cc1:Pane runat="server" ID="pane_language">
|
||||
<cc1:PropertyPanel runat="server" ID="prop_language">
|
||||
<select class="language" name="language" data-bind="options: languages, optionsText: 'Code', optionsValue: 'Id', value: language, optionsCaption: '<%=umbraco.ui.Text("assignDomain", "inherit") %>'"></select>
|
||||
<br /><small><%=umbraco.ui.Text("assignDomain", "setLanguageHelp") %></small>
|
||||
</cc1:PropertyPanel>
|
||||
</cc1:Pane>
|
||||
|
||||
<cc1:Pane runat="server" ID="pane_domains">
|
||||
<cc1:PropertyPanel runat="server">
|
||||
<table class="domains" data-bind="visible: domains().length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%=umbraco.ui.Text("assignDomain", "domain") %></th>
|
||||
<th><%=umbraco.ui.Text("assignDomain", "language") %></th>
|
||||
<th />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-bind="foreach: domains">
|
||||
<tr>
|
||||
<td valign="top"><input class="domain duplicate" data-bind="value: Name, uniqueName: true" /><input type="hidden" value="0" /></td>
|
||||
<td valign="top"><select class="language" data-bind="options: $parent.languages, optionsText: 'Code', optionsValue: 'Id', value: Lang, uniqueName: true"></select></td>
|
||||
<td valign="top"><a href="#" class="remove" data-bind="click: $parent.removeDomain"><%=umbraco.ui.Text("assignDomain", "remove") %></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="addDomain">
|
||||
<tr>
|
||||
<td valign="top"><button data-bind="click: addDomain"><%=umbraco.ui.Text("assignDomain", "addNew") %></button></td>
|
||||
<td class="help"><small><%=umbraco.ui.Text("assignDomain", "domainHelp") %></small></td>
|
||||
</tr>
|
||||
</table>
|
||||
</cc1:PropertyPanel>
|
||||
</cc1:Pane>
|
||||
|
||||
<p>
|
||||
<asp:PlaceHolder runat="server" ID="phSave">
|
||||
<button id="btnSave"><%=umbraco.ui.Text("buttons", "save") %></button>
|
||||
<em><%=umbraco.ui.Text("general", "or")%></em>
|
||||
</asp:PlaceHolder>
|
||||
<a href="#" style="color: #0000ff;" onclick="UmbClientMgr.closeModalWindow()"><%=umbraco.ui.Text("general", "cancel")%></a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</asp:Content>
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.UI.Pages;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Web.WebServices;
|
||||
|
||||
|
||||
namespace umbraco.dialogs
|
||||
{
|
||||
public partial class AssignDomain2 : UmbracoEnsuredPage
|
||||
{
|
||||
protected override void OnLoad(EventArgs e)
|
||||
{
|
||||
base.OnLoad(e);
|
||||
|
||||
var nodeId = GetNodeId();
|
||||
var node = ApplicationContext.Current.Services.ContentService.GetById(nodeId);
|
||||
|
||||
if (node == null)
|
||||
{
|
||||
feedback.Text = ui.Text("assignDomain", "invalidNode");
|
||||
pane_language.Visible = false;
|
||||
pane_domains.Visible = false;
|
||||
phSave.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!UmbracoUser.GetPermissions(node.Path).Contains(BusinessLogic.Actions.ActionAssignDomain.Instance.Letter))
|
||||
{
|
||||
feedback.Text = ui.Text("assignDomain", "permissionDenied");
|
||||
pane_language.Visible = false;
|
||||
pane_domains.Visible = false;
|
||||
phSave.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
pane_language.Text = ui.Text("assignDomain", "setLanguage");
|
||||
pane_domains.Text = ui.Text("assignDomain", "setDomains");
|
||||
prop_language.Text = ui.Text("assignDomain", "language");
|
||||
|
||||
var nodeDomains = DomainHelper.GetNodeDomains(nodeId, true);
|
||||
var wildcard = nodeDomains.FirstOrDefault(d => d.IsWildcard);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append("languages: [");
|
||||
var i = 0;
|
||||
foreach (var language in ApplicationContext.Current.Services.LocalizationService.GetAllLanguages())
|
||||
sb.AppendFormat("{0}{{ \"Id\": {1}, \"Code\": \"{2}\" }}", (i++ == 0 ? "" : ","), language.Id, language.IsoCode);
|
||||
sb.Append("]\r\n");
|
||||
|
||||
sb.AppendFormat(",language: {0}", wildcard == null ? "undefined" : wildcard.Language.id.ToString());
|
||||
|
||||
sb.Append(",domains: [");
|
||||
i = 0;
|
||||
foreach (var domain in nodeDomains.Where(d => !d.IsWildcard))
|
||||
sb.AppendFormat("{0}{{ \"Name\": \"{1}\", \"Lang\": \"{2}\" }}", (i++ == 0 ? "" :","), domain.Name, domain.Language.id);
|
||||
sb.Append("]\r\n");
|
||||
|
||||
data.Text = sb.ToString();
|
||||
}
|
||||
|
||||
protected int GetNodeId()
|
||||
{
|
||||
int nodeId;
|
||||
if (!int.TryParse(Request.QueryString["id"], out nodeId))
|
||||
nodeId = -1;
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
protected string GetRestServicePath()
|
||||
{
|
||||
const string action = "ListDomains";
|
||||
var path = Url.GetUmbracoApiService<DomainsApiController>(action);
|
||||
return path.TrimEnd(action).EnsureEndsWith('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
69
src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain2.aspx.designer.cs
generated
Normal file
69
src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain2.aspx.designer.cs
generated
Normal file
@@ -0,0 +1,69 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace umbraco.dialogs {
|
||||
|
||||
|
||||
public partial class AssignDomain2 {
|
||||
|
||||
/// <summary>
|
||||
/// data control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::System.Web.UI.WebControls.Literal data;
|
||||
|
||||
/// <summary>
|
||||
/// feedback control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::umbraco.uicontrols.Feedback feedback;
|
||||
|
||||
/// <summary>
|
||||
/// pane_language control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::umbraco.uicontrols.Pane pane_language;
|
||||
|
||||
/// <summary>
|
||||
/// prop_language control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::umbraco.uicontrols.PropertyPanel prop_language;
|
||||
|
||||
/// <summary>
|
||||
/// pane_domains control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::umbraco.uicontrols.Pane pane_domains;
|
||||
|
||||
/// <summary>
|
||||
/// phSave control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::System.Web.UI.WebControls.PlaceHolder phSave;
|
||||
}
|
||||
}
|
||||
@@ -390,8 +390,8 @@ namespace umbraco.cms.presentation
|
||||
return;
|
||||
}
|
||||
|
||||
var niceUrlProvider = Umbraco.Web.UmbracoContext.Current.RoutingContext.NiceUrlProvider;
|
||||
var url = niceUrlProvider.GetNiceUrl(_document.Id);
|
||||
var urlProvider = Umbraco.Web.UmbracoContext.Current.RoutingContext.UrlProvider;
|
||||
var url = urlProvider.GetUrl(_document.Id);
|
||||
string niceUrlText = null;
|
||||
var altUrlsText = new System.Text.StringBuilder();
|
||||
|
||||
@@ -415,8 +415,8 @@ namespace umbraco.cms.presentation
|
||||
{
|
||||
niceUrlText = string.Format("<a href=\"{0}\" target=\"_blank\">{0}</a>", url);
|
||||
|
||||
foreach (var altUrl in niceUrlProvider.GetAllAbsoluteNiceUrls(_document.Id).Where(u => u != url))
|
||||
altUrlsText.AppendFormat("<a href=\"{0}\" target=\"_blank\">{0}</a><br />", altUrl);
|
||||
foreach (var otherUrl in urlProvider.GetOtherUrls(_document.Id))
|
||||
altUrlsText.AppendFormat("<a href=\"{0}\" target=\"_blank\">{0}</a><br />", otherUrl);
|
||||
}
|
||||
|
||||
UpdateNiceUrlProperties(niceUrlText, altUrlsText.ToString());
|
||||
|
||||
@@ -147,7 +147,12 @@ namespace umbraco.cms.businesslogic.web
|
||||
|
||||
internal static List<Domain> GetDomains()
|
||||
{
|
||||
return Cache.GetCacheItem<List<Domain>>("UmbracoDomainList", getDomainsSyncLock, TimeSpan.FromMinutes(30),
|
||||
return GetDomains(false);
|
||||
}
|
||||
|
||||
internal static List<Domain> GetDomains(bool includeWildcards)
|
||||
{
|
||||
var domains = Cache.GetCacheItem<List<Domain>>("UmbracoDomainList", getDomainsSyncLock, TimeSpan.FromMinutes(30),
|
||||
delegate
|
||||
{
|
||||
List<Domain> result = new List<Domain>();
|
||||
@@ -170,6 +175,11 @@ namespace umbraco.cms.businesslogic.web
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
if (!includeWildcards)
|
||||
domains = domains.Where(d => !d.IsWildcard).ToList();
|
||||
|
||||
return domains;
|
||||
}
|
||||
|
||||
public static Domain GetDomain(string DomainName)
|
||||
@@ -259,5 +269,23 @@ namespace umbraco.cms.businesslogic.web
|
||||
if (AfterDelete != null)
|
||||
AfterDelete(this, e);
|
||||
}
|
||||
|
||||
#region Pipeline Refactoring
|
||||
|
||||
// NOTE: the wildcard name thing should be managed by the Domain class
|
||||
// internally but that would break too much backward compatibility, so
|
||||
// we don't do it now. Will do it when the Domain class migrates to the
|
||||
// new Core.Models API.
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the domain is a wildcard domain.
|
||||
/// </summary>
|
||||
/// <returns>A value indicating whether the domain is a wildcard domain.</returns>
|
||||
public bool IsWildcard
|
||||
{
|
||||
get { return string.IsNullOrWhiteSpace(Name) || Name.StartsWith("*"); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user