diff --git a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs index b3a0e466f7..0256e2b10f 100644 --- a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs +++ b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs @@ -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(); diff --git a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs b/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs index b56352afd9..73d42a1d7d 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs @@ -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() diff --git a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs index 330c6a3739..e899f930c9 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs @@ -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() @@ -370,7 +374,7 @@ namespace Umbraco.Tests.Routing [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.UrlProvider.GetOtherUrls(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/")); } diff --git a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs index 3d343b2a67..5deee2f194 100644 --- a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs @@ -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() diff --git a/src/Umbraco.Web/Routing/ContentFinderByNiceUrl.cs b/src/Umbraco.Web/Routing/ContentFinderByNiceUrl.cs index 226ec9ffe2..67ab79005f 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByNiceUrl.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByNiceUrl.cs @@ -95,7 +95,7 @@ namespace Umbraco.Web.Routing LogHelper.Debug("Query matches, id={0}", () => docreq.PublishedContent.Id); var rootNodeId = docreq.Domain == null ? (int?) null : docreq.Domain.RootNodeId; - var iscanon = _doDomainLookup && !DomainHelper.ExistsDomainInPath(DomainHelper.GetAllDomains(), node.Path, rootNodeId); + var iscanon = _doDomainLookup && !DomainHelper.ExistsDomainInPath(DomainHelper.GetAllDomains(false), node.Path, rootNodeId); if (!iscanon) LogHelper.Debug("Non canonical url"); diff --git a/src/Umbraco.Web/Routing/DomainHelper.cs b/src/Umbraco.Web/Routing/DomainHelper.cs index 5abb59bc56..191f4153ea 100644 --- a/src/Umbraco.Web/Routing/DomainHelper.cs +++ b/src/Umbraco.Web/Routing/DomainHelper.cs @@ -17,22 +17,24 @@ namespace Umbraco.Web.Routing /// /// Gets all domains defined in the system. /// + /// A value indicating whether to include wildcard domains. /// All domains defined in the system. /// This is to temporarily abstract Umbraco's API. - internal static Domain[] GetAllDomains() + internal static Domain[] GetAllDomains(bool includeWildcards) { - return Domain.GetDomains().ToArray(); + return Domain.GetDomains(includeWildcards).ToArray(); } /// /// Gets all domains defined in the system at a specified node. /// /// The node identifier. + /// A value indicating whether to include wildcard domains. /// All domains defined in the system at the specified node. /// This is to temporarily abstract Umbraco's API. - internal static Domain[] GetNodeDomains(int nodeId) + internal static Domain[] GetNodeDomains(int nodeId, bool includeWildcards) { - return Domain.GetDomainsById(nodeId); + return Domain.GetDomains(includeWildcards).Where(d => d.RootNodeId == nodeId).ToArray(); } #endregion @@ -44,7 +46,9 @@ namespace Umbraco.Web.Routing /// /// The node identifier. /// The uri, or null. - /// The domain and its uri, if any, that best matches the specified uri. + /// The domain and its uri, if any, that best matches the specified uri, else null. + /// 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. internal static DomainAndUri DomainForNode(int nodeId, Uri current) { // be safe @@ -52,13 +56,19 @@ namespace Umbraco.Web.Routing return null; // get the domains on that node - var domains = GetNodeDomains(nodeId); + var domains = GetNodeDomains(nodeId, false); - // filter those that match + // none? + if (!domains.Any()) + return null; + + // else filter var helper = SiteDomainHelperResolver.Current.Helper; var domainAndUri = DomainForUri(domains, current, domainAndUris => helper.MapDomain(current, domainAndUris)); - // return null or the uri + if (domainAndUri == null) + throw new Exception("DomainForUri returned null."); + return domainAndUri; } @@ -67,7 +77,9 @@ namespace Umbraco.Web.Routing /// /// The node identifier. /// The uri, or null. - /// The domains and their uris, that match the specified uri. + /// The domains and their uris, that match the specified uri, else null. + /// 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. internal static IEnumerable DomainsForNode(int nodeId, Uri current) { // be safe @@ -75,15 +87,18 @@ namespace Umbraco.Web.Routing return null; // get the domains on that node - var domains = GetNodeDomains(nodeId); + var domains = GetNodeDomains(nodeId, false); - // filter those that match + // none? + if (!domains.Any()) + return null; + + // get the domains and their uris var domainAndUris = DomainsForUri(domains, current).ToArray(); - // return null or a (maybe empty) enumerable of uris + // filter var helper = SiteDomainHelperResolver.Current.Helper; - var domainAndUris2 = helper.MapDomains(current, domainAndUris).ToArray(); - return domainAndUris2.Any() ? domainAndUris2 : null; + return helper.MapDomains(current, domainAndUris).ToArray(); } #endregion @@ -121,17 +136,16 @@ namespace Umbraco.Web.Routing DomainAndUri domainAndUri; if (current == null) { - // take the first one by default (is that OK?) - domainAndUri = domainsAndUris.First(); + // take the first one by default (what else can we do?) + domainAndUri = domainsAndUris.First(); // .First() protected by .Any() above } else { - // look for a domain that would be the base of the hint - // assume only one can match the hint (is that OK?) + // 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 sort them out + // if none matches, then try to run the filter to pick a domain if (domainAndUri == null && filter != null) { domainAndUri = filter(domainsAndUris); diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs index a800bf017e..4e16f57b40 100644 --- a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs +++ b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs @@ -166,7 +166,7 @@ namespace Umbraco.Web.Routing LogHelper.Debug("{0}Uri=\"{1}\"", () => tracePrefix, () => _pcr.Uri); // try to find a domain matching the current request - var domainAndUri = DomainHelper.DomainForUri(Domain.GetDomains().ToArray(), _pcr.Uri); + 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("{0}Path=\"{1}\"", () => tracePrefix, () => nodePath); var rootNodeId = _pcr.HasDomain ? _pcr.Domain.RootNodeId : (int?)null; - var domain = DomainHelper.FindWildcardDomainInPath(Domain.GetDomains().ToArray(), nodePath, rootNodeId); + var domain = DomainHelper.FindWildcardDomainInPath(DomainHelper.GetAllDomains(true), nodePath, rootNodeId); if (domain != null) { diff --git a/src/Umbraco.Web/Routing/SiteDomainHelper.cs b/src/Umbraco.Web/Routing/SiteDomainHelper.cs index 5a4bbdfea3..69c3a59bfb 100644 --- a/src/Umbraco.Web/Routing/SiteDomainHelper.cs +++ b/src/Umbraco.Web/Routing/SiteDomainHelper.cs @@ -208,15 +208,23 @@ namespace Umbraco.Web.Routing IEnumerable ret; using (ConfigReadLock) // so nothing changes between GetQualifiedSites and access to bindings - { + { var qualifiedSites = GetQualifiedSitesInsideLock(current); // exclude the current one (avoid producing the absolute equivalent of what GetUrl returns) - ret = domainAndUris.Where(d => d.Uri.GetLeftPart(UriPartial.Authority) != currentAuthority); + var hintWithSlash = current.EndPathWithSlash(); + var hinted = domainAndUris.FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(hintWithSlash)); + ret = hinted == null ? domainAndUris : domainAndUris.Where(d => d != hinted); // exclude the default one (avoid producing a possible duplicate of what GetUrl returns) - var mainDomain = MapDomain(domainAndUris, qualifiedSites, currentAuthority); // what GetUrl would get - ret = ret.Where(d => d != mainDomain); + // 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) @@ -280,6 +288,11 @@ namespace Umbraco.Web.Routing private static DomainAndUri MapDomain(DomainAndUri[] domainAndUris, Dictionary 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(); diff --git a/src/Umbraco.Web/WebServices/DomainsApiController.cs b/src/Umbraco.Web/WebServices/DomainsApiController.cs index 3e166b7ad5..fd53e7ad95 100644 --- a/src/Umbraco.Web/WebServices/DomainsApiController.cs +++ b/src/Umbraco.Web/WebServices/DomainsApiController.cs @@ -17,29 +17,6 @@ namespace Umbraco.Web.WebServices /// Nothing to do with Active Directory. public class DomainsApiController : UmbracoAuthorizedApiController { - [HttpGet] - public DomainModel[] ListDomains(int nodeId) - { - var node = ApplicationContext.Current.Services.ContentService.GetById(nodeId); - - if (node == null) - throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.BadRequest) - { - Content = new StringContent(string.Format("There is no content node with id {0}.", 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." - }); - - var domains = Routing.DomainHelper.GetNodeDomains(nodeId); - return domains.Select(d => new DomainModel(d.Name, d.Language.id)).ToArray(); - } - [HttpPost] // can't pass multiple complex args in json post request... public PostBackModel SaveLanguageAndDomains(PostBackModel model) @@ -61,7 +38,7 @@ namespace Umbraco.Web.WebServices }); model.Valid = true; - var domains = Routing.DomainHelper.GetNodeDomains(model.NodeId); + 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; diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain2.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain2.aspx.cs index 672340dd60..600078a78d 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain2.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain2.aspx.cs @@ -43,7 +43,7 @@ namespace umbraco.dialogs pane_domains.Text = ui.Text("assignDomain", "setDomains"); prop_language.Text = ui.Text("assignDomain", "language"); - var nodeDomains = DomainHelper.GetNodeDomains(nodeId); + var nodeDomains = DomainHelper.GetNodeDomains(nodeId, true); var wildcard = nodeDomains.FirstOrDefault(d => d.IsWildcard); var sb = new StringBuilder(); diff --git a/src/umbraco.cms/businesslogic/web/Domain.cs b/src/umbraco.cms/businesslogic/web/Domain.cs index 2782a04b61..db59dc3d1e 100644 --- a/src/umbraco.cms/businesslogic/web/Domain.cs +++ b/src/umbraco.cms/businesslogic/web/Domain.cs @@ -147,7 +147,12 @@ namespace umbraco.cms.businesslogic.web internal static List GetDomains() { - return Cache.GetCacheItem>("UmbracoDomainList", getDomainsSyncLock, TimeSpan.FromMinutes(30), + return GetDomains(false); + } + + internal static List GetDomains(bool includeWildcards) + { + var domains = Cache.GetCacheItem>("UmbracoDomainList", getDomainsSyncLock, TimeSpan.FromMinutes(30), delegate { List result = new List(); @@ -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)