Merge remote-tracking branch 'origin/v11/dev' into v11/dev
This commit is contained in:
@@ -2,18 +2,58 @@ using Umbraco.Cms.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a domain name, optionally assigned to a content and/or language ID.
|
||||
/// </summary>
|
||||
/// <seealso cref="Umbraco.Cms.Core.Models.Entities.IEntity" />
|
||||
/// <seealso cref="Umbraco.Cms.Core.Models.Entities.IRememberBeingDirty" />
|
||||
public interface IDomain : IEntity, IRememberBeingDirty
|
||||
{
|
||||
int? LanguageId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the domain.
|
||||
/// </value>
|
||||
string DomainName { get; set; }
|
||||
|
||||
int? RootContentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this is a wildcard domain (only specifying the language of a content node).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this is a wildcard domain; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
bool IsWildcard { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Readonly value of the language ISO code for the domain
|
||||
/// Gets or sets the language ID assigned to the domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The language ID assigned to the domain.
|
||||
/// </value>
|
||||
int? LanguageId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the language ISO code.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The language ISO code.
|
||||
/// </value>
|
||||
string? LanguageIsoCode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the root content ID assigned to the domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The root content ID assigned to the domain.
|
||||
/// </value>
|
||||
int? RootContentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sort order.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The sort order.
|
||||
/// </value>
|
||||
int SortOrder { get => IsWildcard ? -1 : 0; set { } } // TODO Remove default implementation in a future version
|
||||
}
|
||||
|
||||
@@ -17,16 +17,20 @@ public struct Fallback : IEnumerable<int>
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Fallback" /> struct with values.
|
||||
/// </summary>
|
||||
/// <param name="values">The values.</param>
|
||||
private Fallback(int[] values) => _values = values;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an ordered set of fallback policies.
|
||||
/// </summary>
|
||||
/// <param name="values"></param>
|
||||
/// <param name="values">The values.</param>
|
||||
/// <returns>
|
||||
/// The fallback policy.
|
||||
/// </returns>
|
||||
public static Fallback To(params int[] values) => new(values);
|
||||
|
||||
/// <summary>
|
||||
/// Fallback to default value.
|
||||
/// Fallback to the default value.
|
||||
/// </summary>
|
||||
public const int DefaultValue = 1;
|
||||
|
||||
@@ -41,18 +45,40 @@ public struct Fallback : IEnumerable<int>
|
||||
public const int Ancestors = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the fallback to default value policy.
|
||||
/// Fallback to the default language.
|
||||
/// </summary>
|
||||
public const int DefaultLanguage = 4;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the fallback to the default language policy.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The default language fallback policy.
|
||||
/// </value>
|
||||
public static Fallback ToDefaultLanguage => new Fallback(new[] { DefaultLanguage });
|
||||
|
||||
/// <summary>
|
||||
/// Gets the fallback to the default value policy.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The default value fallback policy.
|
||||
/// </value>
|
||||
public static Fallback ToDefaultValue => new(new[] { DefaultValue });
|
||||
|
||||
/// <summary>
|
||||
/// Gets the fallback to language policy.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The language fallback policy.
|
||||
/// </value>
|
||||
public static Fallback ToLanguage => new(new[] { Language });
|
||||
|
||||
/// <summary>
|
||||
/// Gets the fallback to tree ancestors policy.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The tree ancestors fallback policy.
|
||||
/// </value>
|
||||
public static Fallback ToAncestors => new(new[] { Ancestors });
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -44,6 +44,13 @@ public class PublishedValueFallback : IPublishedValueFallback
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
case Fallback.DefaultLanguage:
|
||||
if (TryGetValueWithDefaultLanguageFallback(property, culture, segment, out value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw NotSupportedFallbackMethod(f, "property");
|
||||
@@ -85,6 +92,13 @@ public class PublishedValueFallback : IPublishedValueFallback
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
case Fallback.DefaultLanguage:
|
||||
if (TryGetValueWithDefaultLanguageFallback(content, alias, culture, segment, out value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw NotSupportedFallbackMethod(f, "element");
|
||||
@@ -141,6 +155,13 @@ public class PublishedValueFallback : IPublishedValueFallback
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
case Fallback.DefaultLanguage:
|
||||
if (TryGetValueWithDefaultLanguageFallback(content, alias, culture, segment, out value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw NotSupportedFallbackMethod(f, "content");
|
||||
@@ -347,4 +368,42 @@ public class PublishedValueFallback : IPublishedValueFallback
|
||||
language = language2;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetValueWithDefaultLanguageFallback<T>(IPublishedProperty property, string? culture, string? segment, out T? value)
|
||||
{
|
||||
value = default;
|
||||
|
||||
if (culture.IsNullOrWhiteSpace())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string? defaultCulture = _localizationService?.GetDefaultLanguageIsoCode();
|
||||
if (culture.InvariantEquals(defaultCulture) == false && property.HasValue(defaultCulture, segment))
|
||||
{
|
||||
value = property.Value<T>(this, defaultCulture, segment);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetValueWithDefaultLanguageFallback<T>(IPublishedElement element, string alias, string? culture, string? segment, out T? value)
|
||||
{
|
||||
value = default;
|
||||
|
||||
if (culture.IsNullOrWhiteSpace())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string? defaultCulture = _localizationService?.GetDefaultLanguageIsoCode();
|
||||
if (culture.InvariantEquals(defaultCulture) == false && element.HasValue(alias, defaultCulture, segment))
|
||||
{
|
||||
value = element.Value<T>(this, alias, defaultCulture, segment);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,27 +3,33 @@ using Umbraco.Cms.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models;
|
||||
|
||||
/// <inheritdoc />
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class UmbracoDomain : EntityBase, IDomain
|
||||
{
|
||||
private int? _contentId;
|
||||
private string _domainName;
|
||||
private int? _languageId;
|
||||
private int? _rootContentId;
|
||||
private int _sortOrder;
|
||||
|
||||
public UmbracoDomain(string domainName) => _domainName = domainName;
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UmbracoDomain" /> class.
|
||||
/// </summary>
|
||||
/// <param name="domainName">The name of the domain.</param>
|
||||
public UmbracoDomain(string domainName)
|
||||
=> _domainName = domainName;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UmbracoDomain" /> class.
|
||||
/// </summary>
|
||||
/// <param name="domainName">The name of the domain.</param>
|
||||
/// <param name="languageIsoCode">The language ISO code.</param>
|
||||
public UmbracoDomain(string domainName, string languageIsoCode)
|
||||
: this(domainName) =>
|
||||
LanguageIsoCode = languageIsoCode;
|
||||
|
||||
[DataMember]
|
||||
public int? LanguageId
|
||||
{
|
||||
get => _languageId;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _languageId, nameof(LanguageId));
|
||||
}
|
||||
: this(domainName)
|
||||
=> LanguageIsoCode = languageIsoCode;
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public string DomainName
|
||||
{
|
||||
@@ -31,17 +37,33 @@ public class UmbracoDomain : EntityBase, IDomain
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _domainName!, nameof(DomainName));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsWildcard => string.IsNullOrWhiteSpace(DomainName) || DomainName.StartsWith("*");
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public int? LanguageId
|
||||
{
|
||||
get => _languageId;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _languageId, nameof(LanguageId));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? LanguageIsoCode { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public int? RootContentId
|
||||
{
|
||||
get => _contentId;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _contentId, nameof(RootContentId));
|
||||
get => _rootContentId;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _rootContentId, nameof(RootContentId));
|
||||
}
|
||||
|
||||
public bool IsWildcard => string.IsNullOrWhiteSpace(DomainName) || DomainName.StartsWith("*");
|
||||
|
||||
/// <summary>
|
||||
/// Readonly value of the language ISO code for the domain
|
||||
/// </summary>
|
||||
public string? LanguageIsoCode { get; set; }
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public int SortOrder
|
||||
{
|
||||
get => _sortOrder;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _sortOrder, nameof(SortOrder));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,13 +13,28 @@ public class Domain
|
||||
/// <param name="contentId">The identifier of the content which supports the domain.</param>
|
||||
/// <param name="culture">The culture of the domain.</param>
|
||||
/// <param name="isWildcard">A value indicating whether the domain is a wildcard domain.</param>
|
||||
[Obsolete("Use the constructor specifying all properties instead. This constructor will be removed in a future version.")]
|
||||
public Domain(int id, string name, int contentId, string? culture, bool isWildcard)
|
||||
: this(id, name, contentId, culture, isWildcard, -1)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Domain" /> class.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the domain.</param>
|
||||
/// <param name="name">The name of the domain.</param>
|
||||
/// <param name="contentId">The identifier of the content which supports the domain.</param>
|
||||
/// <param name="culture">The culture of the domain.</param>
|
||||
/// <param name="isWildcard">A value indicating whether the domain is a wildcard domain.</param>
|
||||
/// <param name="sortOrder">The sort order.</param>
|
||||
public Domain(int id, string name, int contentId, string? culture, bool isWildcard, int sortOrder)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
ContentId = contentId;
|
||||
Culture = culture;
|
||||
IsWildcard = isWildcard;
|
||||
SortOrder = sortOrder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -33,30 +48,54 @@ public class Domain
|
||||
ContentId = domain.ContentId;
|
||||
Culture = domain.Culture;
|
||||
IsWildcard = domain.IsWildcard;
|
||||
SortOrder = domain.SortOrder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique identifier of the domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The unique identifier of the domain.
|
||||
/// </value>
|
||||
public int Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the domain.
|
||||
/// </value>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the identifier of the content which supports the domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The identifier of the content which supports the domain.
|
||||
/// </value>
|
||||
public int ContentId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the culture of the domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The culture of the domain.
|
||||
/// </value>
|
||||
public string? Culture { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the domain is a wildcard domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this is a wildcard domain; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsWildcard { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sort order.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The sort order.
|
||||
/// </value>
|
||||
public int SortOrder { get; }
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Web;
|
||||
@@ -172,14 +173,10 @@ namespace Umbraco.Cms.Core.Routing
|
||||
// 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 domainsAndUris = domains?
|
||||
.Where(d => d.IsWildcard == false)
|
||||
.Select(d => new DomainAndUri(d, uri))
|
||||
.OrderByDescending(d => d.Uri.ToString())
|
||||
.ToList();
|
||||
DomainAndUri[]? domainsAndUris = SelectDomains(domains, uri)?.ToArray();
|
||||
|
||||
// nothing = no magic, return null
|
||||
if (domainsAndUris is null || domainsAndUris.Count == 0)
|
||||
if (domainsAndUris is null || domainsAndUris.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -204,8 +201,9 @@ namespace Umbraco.Cms.Core.Routing
|
||||
IReadOnlyCollection<DomainAndUri> considerForBaseDomains = domainsAndUris;
|
||||
if (cultureDomains != null)
|
||||
{
|
||||
if (cultureDomains.Count == 1) // only 1, return
|
||||
if (cultureDomains.Count == 1)
|
||||
{
|
||||
// only 1, return
|
||||
return cultureDomains.First();
|
||||
}
|
||||
|
||||
@@ -214,9 +212,11 @@ namespace Umbraco.Cms.Core.Routing
|
||||
}
|
||||
|
||||
// look for domains that would be the base of the uri
|
||||
IReadOnlyCollection<DomainAndUri> baseDomains = SelectByBase(considerForBaseDomains, uri, culture);
|
||||
if (baseDomains.Count > 0) // found, return
|
||||
// we need to order so example.com/foo matches before example.com/
|
||||
IReadOnlyCollection<DomainAndUri> baseDomains = SelectByBase(considerForBaseDomains.OrderByDescending(d => d.Uri.ToString()).ToList(), uri, culture);
|
||||
if (baseDomains.Count > 0)
|
||||
{
|
||||
// found, return
|
||||
return baseDomains.First();
|
||||
}
|
||||
|
||||
@@ -246,9 +246,9 @@ namespace Umbraco.Cms.Core.Routing
|
||||
|
||||
// if none matches, try again without the port
|
||||
// ie current is www.example.com:1234/foo/bar, look for domain www.example.com
|
||||
Uri currentWithoutPort = currentWithSlash.WithoutPort();
|
||||
if (baseDomains.Count == 0)
|
||||
{
|
||||
Uri currentWithoutPort = currentWithSlash.WithoutPort();
|
||||
baseDomains = domainsAndUris.Where(d => IsBaseOf(d, currentWithoutPort)).ToList();
|
||||
}
|
||||
|
||||
@@ -258,9 +258,9 @@ namespace Umbraco.Cms.Core.Routing
|
||||
private static IReadOnlyCollection<DomainAndUri>? SelectByCulture(IReadOnlyCollection<DomainAndUri> domainsAndUris, string? culture, string? defaultCulture)
|
||||
{
|
||||
// we try our best to match cultures, but may end with a bogus domain
|
||||
|
||||
if (culture != null) // try the supplied culture
|
||||
if (culture is not null)
|
||||
{
|
||||
// try the supplied culture
|
||||
var cultureDomains = domainsAndUris.Where(x => x.Culture.InvariantEquals(culture)).ToList();
|
||||
if (cultureDomains.Count > 0)
|
||||
{
|
||||
@@ -268,8 +268,9 @@ namespace Umbraco.Cms.Core.Routing
|
||||
}
|
||||
}
|
||||
|
||||
if (defaultCulture != null) // try the defaultCulture culture
|
||||
if (defaultCulture is not null)
|
||||
{
|
||||
// try the defaultCulture culture
|
||||
var cultureDomains = domainsAndUris.Where(x => x.Culture.InvariantEquals(defaultCulture)).ToList();
|
||||
if (cultureDomains.Count > 0)
|
||||
{
|
||||
@@ -280,31 +281,32 @@ namespace Umbraco.Cms.Core.Routing
|
||||
return null;
|
||||
}
|
||||
|
||||
private static DomainAndUri GetByCulture(IReadOnlyCollection<DomainAndUri> domainsAndUris, string? culture, string? defaultCulture)
|
||||
private static DomainAndUri? GetByCulture(IReadOnlyCollection<DomainAndUri> domainsAndUris, string? culture, string? defaultCulture)
|
||||
{
|
||||
DomainAndUri? domainAndUri;
|
||||
|
||||
// we try our best to match cultures, but may end with a bogus domain
|
||||
|
||||
if (culture != null) // try the supplied culture
|
||||
if (culture is not null)
|
||||
{
|
||||
// try the supplied culture
|
||||
domainAndUri = domainsAndUris.FirstOrDefault(x => x.Culture.InvariantEquals(culture));
|
||||
if (domainAndUri != null)
|
||||
if (domainAndUri is not null)
|
||||
{
|
||||
return domainAndUri;
|
||||
}
|
||||
}
|
||||
|
||||
if (defaultCulture != null) // try the defaultCulture culture
|
||||
if (defaultCulture is not null)
|
||||
{
|
||||
// try the defaultCulture culture
|
||||
domainAndUri = domainsAndUris.FirstOrDefault(x => x.Culture.InvariantEquals(defaultCulture));
|
||||
if (domainAndUri != null)
|
||||
if (domainAndUri is not null)
|
||||
{
|
||||
return domainAndUri;
|
||||
}
|
||||
}
|
||||
|
||||
return domainsAndUris.First(); // what else?
|
||||
return domainsAndUris.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -313,14 +315,10 @@ namespace Umbraco.Cms.Core.Routing
|
||||
/// <param name="domains">The domains.</param>
|
||||
/// <param name="uri">The uri, or null.</param>
|
||||
/// <returns>The domains and their normalized uris, that match the specified uri.</returns>
|
||||
internal static IEnumerable<DomainAndUri> SelectDomains(IEnumerable<Domain> domains, Uri uri)
|
||||
{
|
||||
[return: NotNullIfNotNull(nameof(domains))]
|
||||
private static IEnumerable<DomainAndUri>? SelectDomains(IEnumerable<Domain>? domains, Uri uri)
|
||||
// TODO: where are we matching ?!!?
|
||||
return domains
|
||||
.Where(d => d.IsWildcard == false)
|
||||
.Select(d => new DomainAndUri(d, uri))
|
||||
.OrderByDescending(d => d.Uri.ToString());
|
||||
}
|
||||
=> domains?.Where(d => d.IsWildcard == false).Select(d => new DomainAndUri(d, uri));
|
||||
|
||||
/// <summary>
|
||||
/// Parses a domain name into a URI.
|
||||
@@ -351,9 +349,7 @@ namespace Umbraco.Cms.Core.Routing
|
||||
/// <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(IEnumerable<Domain> domains, string path, int? rootNodeId)
|
||||
{
|
||||
return FindDomainInPath(domains, path, rootNodeId) != null;
|
||||
}
|
||||
=> FindDomainInPath(domains, path, rootNodeId) is not null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the deepest non-wildcard Domain, if any, from a group of Domains, in a node path.
|
||||
@@ -364,17 +360,7 @@ namespace Umbraco.Cms.Core.Routing
|
||||
/// <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(IEnumerable<Domain> domains, string path, int? rootNodeId)
|
||||
{
|
||||
var stopNodeId = rootNodeId ?? -1;
|
||||
|
||||
return path.Split(Constants.CharArrays.Comma)
|
||||
.Reverse()
|
||||
.Select(s => int.Parse(s, CultureInfo.InvariantCulture))
|
||||
.TakeWhile(id => id != stopNodeId)
|
||||
.Select(id => domains.FirstOrDefault(d => d.ContentId == id && d.IsWildcard == false))
|
||||
.SkipWhile(domain => domain == null)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
=> FindDomainInPath(domains, path, rootNodeId, false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the deepest wildcard Domain, if any, from a group of Domains, in a node path.
|
||||
@@ -385,6 +371,9 @@ namespace Umbraco.Cms.Core.Routing
|
||||
/// <returns>The deepest wildcard Domain in the path, or null.</returns>
|
||||
/// <remarks>Looks _under_ rootNodeId but not _at_ rootNodeId.</remarks>
|
||||
public static Domain? FindWildcardDomainInPath(IEnumerable<Domain>? domains, string path, int? rootNodeId)
|
||||
=> FindDomainInPath(domains, path, rootNodeId, true);
|
||||
|
||||
private static Domain? FindDomainInPath(IEnumerable<Domain>? domains, string path, int? rootNodeId, bool isWildcard)
|
||||
{
|
||||
var stopNodeId = rootNodeId ?? -1;
|
||||
|
||||
@@ -392,8 +381,8 @@ namespace Umbraco.Cms.Core.Routing
|
||||
.Reverse()
|
||||
.Select(s => int.Parse(s, CultureInfo.InvariantCulture))
|
||||
.TakeWhile(id => id != stopNodeId)
|
||||
.Select(id => domains?.FirstOrDefault(d => d.ContentId == id && d.IsWildcard))
|
||||
.FirstOrDefault(domain => domain != null);
|
||||
.Select(id => domains?.FirstOrDefault(d => d.ContentId == id && d.IsWildcard == isWildcard))
|
||||
.FirstOrDefault(domain => domain is not null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -16,8 +16,8 @@ public class DomainService : RepositoryService, IDomainService
|
||||
ILoggerFactory loggerFactory,
|
||||
IEventMessagesFactory eventMessagesFactory,
|
||||
IDomainRepository domainRepository)
|
||||
: base(provider, loggerFactory, eventMessagesFactory) =>
|
||||
_domainRepository = domainRepository;
|
||||
: base(provider, loggerFactory, eventMessagesFactory)
|
||||
=> _domainRepository = domainRepository;
|
||||
|
||||
public bool Exists(string domainName)
|
||||
{
|
||||
@@ -43,8 +43,7 @@ public class DomainService : RepositoryService, IDomainService
|
||||
_domainRepository.Delete(domain);
|
||||
scope.Complete();
|
||||
|
||||
scope.Notifications.Publish(
|
||||
new DomainDeletedNotification(domain, eventMessages).WithStateFrom(deletingNotification));
|
||||
scope.Notifications.Publish(new DomainDeletedNotification(domain, eventMessages).WithStateFrom(deletingNotification));
|
||||
}
|
||||
|
||||
return OperationResult.Attempt.Succeed(eventMessages);
|
||||
@@ -97,8 +96,50 @@ public class DomainService : RepositoryService, IDomainService
|
||||
|
||||
_domainRepository.Save(domainEntity);
|
||||
scope.Complete();
|
||||
scope.Notifications.Publish(
|
||||
new DomainSavedNotification(domainEntity, eventMessages).WithStateFrom(savingNotification));
|
||||
|
||||
scope.Notifications.Publish(new DomainSavedNotification(domainEntity, eventMessages).WithStateFrom(savingNotification));
|
||||
}
|
||||
|
||||
return OperationResult.Attempt.Succeed(eventMessages);
|
||||
}
|
||||
|
||||
public Attempt<OperationResult?> Sort(IEnumerable<IDomain> items)
|
||||
{
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
|
||||
IDomain[] domains = items.ToArray();
|
||||
if (domains.Length == 0)
|
||||
{
|
||||
return OperationResult.Attempt.NoOperation(eventMessages);
|
||||
}
|
||||
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
|
||||
{
|
||||
var savingNotification = new DomainSavingNotification(domains, eventMessages);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Attempt.Cancel(eventMessages);
|
||||
}
|
||||
|
||||
scope.WriteLock(Constants.Locks.Domains);
|
||||
|
||||
int sortOrder = 0;
|
||||
foreach (IDomain domain in domains)
|
||||
{
|
||||
// If the current sort order equals that of the domain we don't need to update it, so just increment the sort order and continue
|
||||
if (domain.SortOrder == sortOrder)
|
||||
{
|
||||
sortOrder++;
|
||||
continue;
|
||||
}
|
||||
|
||||
domain.SortOrder = sortOrder++;
|
||||
_domainRepository.Save(domain);
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
scope.Notifications.Publish(new DomainSavedNotification(domains, eventMessages).WithStateFrom(savingNotification));
|
||||
}
|
||||
|
||||
return OperationResult.Attempt.Succeed(eventMessages);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services;
|
||||
@@ -17,4 +18,7 @@ public interface IDomainService : IService
|
||||
IEnumerable<IDomain> GetAssignedDomains(int contentId, bool includeWildcards);
|
||||
|
||||
Attempt<OperationResult?> Save(IDomain domainEntity);
|
||||
|
||||
Attempt<OperationResult?> Sort(IEnumerable<IDomain> items)
|
||||
=> Attempt.Fail(new OperationResult(OperationResultType.Failed, new EventMessages())); // TODO Remove default implmentation in a future version
|
||||
}
|
||||
|
||||
@@ -1,66 +1,46 @@
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_0_0;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_2_0;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_5_0;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the Umbraco CMS migration plan.
|
||||
/// </summary>
|
||||
/// <seealso cref="MigrationPlan" />
|
||||
/// <seealso cref="Umbraco.Cms.Infrastructure.Migrations.MigrationPlan" />
|
||||
public class UmbracoPlan : MigrationPlan
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UmbracoPlan" /> class.
|
||||
/// </summary>
|
||||
/// <param name="umbracoVersion">The Umbraco version.</param>
|
||||
public UmbracoPlan(IUmbracoVersion umbracoVersion)
|
||||
public UmbracoPlan(IUmbracoVersion umbracoVersion) // TODO (V12): Remove unused parameter
|
||||
: base(Constants.Conventions.Migrations.UmbracoUpgradePlanName)
|
||||
{
|
||||
DefinePlan();
|
||||
}
|
||||
=> DefinePlan();
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// <para>The default initial state in plans is string.Empty.</para>
|
||||
/// <para>
|
||||
/// When upgrading from version 7, we want to use specific initial states
|
||||
/// that are e.g. "{init-7.9.3}", "{init-7.11.1}", etc. so we can chain the proper
|
||||
/// migrations.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This is also where we detect the current version, and reject invalid
|
||||
/// upgrades (from a tool old version, or going back in time, etc).
|
||||
/// </para>
|
||||
/// This is set to the final migration state of 9.4, making that the lowest supported version to upgrade from.
|
||||
/// </remarks>
|
||||
public override string InitialState => "{DED98755-4059-41BB-ADBD-3FEAB12D1D7B}";
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Defines the plan.
|
||||
/// </summary>
|
||||
protected void DefinePlan()
|
||||
{
|
||||
// MODIFYING THE PLAN
|
||||
//
|
||||
// Please take great care when modifying the plan!
|
||||
//
|
||||
// * Creating a migration for version 8:
|
||||
// Append the migration to the main chain, using a new guid, before the "//FINAL" comment
|
||||
//
|
||||
// Creating a migration: append the migration to the main chain, using a new GUID.
|
||||
//
|
||||
// If the new migration causes a merge conflict, because someone else also added another
|
||||
// new migration, you NEED to fix the conflict by providing one default path, and paths
|
||||
// out of the conflict states, eg:
|
||||
//
|
||||
// .From("state-1")
|
||||
// .To<ChangeA>("state-a")
|
||||
// .To<ChangeB>("state-b") // Some might already be in this state, without having applied ChangeA
|
||||
// From("state-1")
|
||||
// To<ChangeA>("state-a")
|
||||
// To<ChangeB>("state-b") // Some might already be in this state, without having applied ChangeA
|
||||
//
|
||||
// .From("state-1")
|
||||
// From("state-1")
|
||||
// .Merge()
|
||||
// .To<ChangeA>("state-a")
|
||||
// .With()
|
||||
@@ -69,12 +49,12 @@ public class UmbracoPlan : MigrationPlan
|
||||
|
||||
From(InitialState);
|
||||
|
||||
// TO 10.0.0
|
||||
To<AddMemberPropertiesAsColumns>("{B7E0D53C-2B0E-418B-AB07-2DDE486E225F}");
|
||||
// To 10.0.0
|
||||
To<V_10_0_0.AddMemberPropertiesAsColumns>("{B7E0D53C-2B0E-418B-AB07-2DDE486E225F}");
|
||||
|
||||
// TO 10.2.0
|
||||
To<AddUserGroup2LanguageTable>("{D0B3D29D-F4D5-43E3-BA67-9D49256F3266}");
|
||||
To<AddHasAccessToAllLanguagesColumn>("{79D8217B-5920-4C0E-8E9A-3CF8FA021882}");
|
||||
// To 10.2.0
|
||||
To<V_10_2_0.AddUserGroup2LanguageTable>("{D0B3D29D-F4D5-43E3-BA67-9D49256F3266}");
|
||||
To<V_10_2_0.AddHasAccessToAllLanguagesColumn>("{79D8217B-5920-4C0E-8E9A-3CF8FA021882}");
|
||||
|
||||
// To 10.3.0
|
||||
To<V_10_3_0.AddBlockGridPartialViews>("{56833770-3B7E-4FD5-A3B6-3416A26A7A3F}");
|
||||
@@ -82,7 +62,10 @@ public class UmbracoPlan : MigrationPlan
|
||||
// To 10.4.0
|
||||
To<V_10_4_0.AddBlockGridPartialViews>("{3F5D492A-A3DB-43F9-A73E-9FEE3B180E6C}");
|
||||
|
||||
// to 10.5.0 / 11.2.0
|
||||
To<AddPrimaryKeyConstrainToContentVersionCleanupDtos>("{83AF7945-DADE-4A02-9041-F3F6EBFAC319}");
|
||||
// To 10.5.0 / 11.2.0
|
||||
To<V_10_5_0.AddPrimaryKeyConstrainToContentVersionCleanupDtos>("{83AF7945-DADE-4A02-9041-F3F6EBFAC319}");
|
||||
|
||||
// To 11.3.0
|
||||
To<V_11_3_0.AddDomainSortOrder>("{BB3889ED-E2DE-49F2-8F71-5FD8616A2661}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_11_3_0;
|
||||
|
||||
public class AddDomainSortOrder : MigrationBase
|
||||
{
|
||||
public AddDomainSortOrder(IMigrationContext context)
|
||||
: base(context)
|
||||
{ }
|
||||
|
||||
protected override void Migrate()
|
||||
{
|
||||
if (ColumnExists(DomainDto.TableName, "sortOrder") == false)
|
||||
{
|
||||
// Use a custom SQL query to prevent selecting explicit columns (sortOrder doesn't exist yet)
|
||||
List<DomainDto> domainDtos = Database.Fetch<DomainDto>($"SELECT * FROM {DomainDto.TableName}");
|
||||
|
||||
Delete.Table(DomainDto.TableName).Do();
|
||||
Create.Table<DomainDto>().Do();
|
||||
|
||||
foreach (DomainDto domainDto in domainDtos)
|
||||
{
|
||||
bool isWildcard = string.IsNullOrWhiteSpace(domainDto.DomainName) || domainDto.DomainName.StartsWith("*");
|
||||
if (isWildcard)
|
||||
{
|
||||
// Set sort order of wildcard domains to -1
|
||||
domainDto.SortOrder = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep exising sort order by setting it to the id
|
||||
domainDto.SortOrder = domainDto.Id;
|
||||
}
|
||||
}
|
||||
|
||||
Database.InsertBatch(domainDtos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
||||
|
||||
[TableName(Constants.DatabaseSchema.Tables.Domain)]
|
||||
[TableName(TableName)]
|
||||
[PrimaryKey("id")]
|
||||
[ExplicitColumns]
|
||||
internal class DomainDto
|
||||
{
|
||||
public const string TableName = Constants.DatabaseSchema.Tables.Domain;
|
||||
|
||||
[Column("id")]
|
||||
[PrimaryKeyColumn]
|
||||
public int Id { get; set; }
|
||||
@@ -26,8 +28,11 @@ internal class DomainDto
|
||||
public string DomainName { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Used for a result on the query to get the associated language for a domain if there is one
|
||||
/// Used for a result on the query to get the associated language for a domain, if there is one.
|
||||
/// </summary>
|
||||
[ResultColumn("languageISOCode")]
|
||||
public string IsoCode { get; set; } = null!;
|
||||
|
||||
[Column("sortOrder")]
|
||||
public int SortOrder { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Persistence.Factories;
|
||||
|
||||
internal static class DomainFactory
|
||||
{
|
||||
public static IDomain BuildEntity(DomainDto dto)
|
||||
{
|
||||
var domain = new UmbracoDomain(dto.DomainName, dto.IsoCode)
|
||||
{
|
||||
Id = dto.Id,
|
||||
LanguageId = dto.DefaultLanguage,
|
||||
RootContentId = dto.RootStructureId,
|
||||
SortOrder = dto.SortOrder,
|
||||
};
|
||||
|
||||
// Reset dirty initial properties (U4-1946)
|
||||
domain.ResetDirtyProperties(false);
|
||||
|
||||
return domain;
|
||||
}
|
||||
|
||||
public static DomainDto BuildDto(IDomain entity)
|
||||
{
|
||||
var dto = new DomainDto
|
||||
{
|
||||
Id = entity.Id,
|
||||
DefaultLanguage = entity.LanguageId,
|
||||
RootStructureId = entity.RootContentId,
|
||||
DomainName = entity.DomainName,
|
||||
SortOrder = entity.SortOrder,
|
||||
};
|
||||
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
@@ -18,5 +18,6 @@ public sealed class DomainMapper : BaseMapper
|
||||
DefineMap<UmbracoDomain, DomainDto>(nameof(UmbracoDomain.RootContentId), nameof(DomainDto.RootStructureId));
|
||||
DefineMap<UmbracoDomain, DomainDto>(nameof(UmbracoDomain.LanguageId), nameof(DomainDto.DefaultLanguage));
|
||||
DefineMap<UmbracoDomain, DomainDto>(nameof(UmbracoDomain.DomainName), nameof(DomainDto.DomainName));
|
||||
DefineMap<UmbracoDomain, DomainDto>(nameof(UmbracoDomain.SortOrder), nameof(DomainDto.SortOrder));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,40 +7,36 @@ using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Persistence.Querying;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Factories;
|
||||
using Umbraco.Cms.Infrastructure.Scoping;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement;
|
||||
|
||||
// TODO: We need to get a readonly ISO code for the domain assigned
|
||||
internal class DomainRepository : EntityRepositoryBase<int, IDomain>, IDomainRepository
|
||||
{
|
||||
public DomainRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger<DomainRepository> logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{
|
||||
}
|
||||
{ }
|
||||
|
||||
public IDomain? GetByName(string domainName) =>
|
||||
GetMany().FirstOrDefault(x => x.DomainName.InvariantEquals(domainName));
|
||||
public IDomain? GetByName(string domainName)
|
||||
=> GetMany().FirstOrDefault(x => x.DomainName.InvariantEquals(domainName));
|
||||
|
||||
public bool Exists(string domainName) => GetMany().Any(x => x.DomainName.InvariantEquals(domainName));
|
||||
public bool Exists(string domainName)
|
||||
=> GetMany().Any(x => x.DomainName.InvariantEquals(domainName));
|
||||
|
||||
public IEnumerable<IDomain> GetAll(bool includeWildcards) =>
|
||||
GetMany().Where(x => includeWildcards || x.IsWildcard == false);
|
||||
public IEnumerable<IDomain> GetAll(bool includeWildcards)
|
||||
=> GetMany().Where(x => includeWildcards || x.IsWildcard == false);
|
||||
|
||||
public IEnumerable<IDomain> GetAssignedDomains(int contentId, bool includeWildcards) =>
|
||||
GetMany()
|
||||
.Where(x => x.RootContentId == contentId)
|
||||
.Where(x => includeWildcards || x.IsWildcard == false);
|
||||
public IEnumerable<IDomain> GetAssignedDomains(int contentId, bool includeWildcards)
|
||||
=> GetMany().Where(x => x.RootContentId == contentId).Where(x => includeWildcards || x.IsWildcard == false);
|
||||
|
||||
protected override IRepositoryCachePolicy<IDomain, int> CreateCachePolicy() =>
|
||||
new FullDataSetRepositoryCachePolicy<IDomain, int>(GlobalIsolatedCache, ScopeAccessor, GetEntityId, /*expires:*/
|
||||
false);
|
||||
protected override IRepositoryCachePolicy<IDomain, int> CreateCachePolicy()
|
||||
=> new FullDataSetRepositoryCachePolicy<IDomain, int>(GlobalIsolatedCache, ScopeAccessor, GetEntityId, false);
|
||||
|
||||
protected override IDomain? PerformGet(int id) =>
|
||||
|
||||
// use the underlying GetAll which will force cache all domains
|
||||
GetMany().FirstOrDefault(x => x.Id == id);
|
||||
protected override IDomain? PerformGet(int id)
|
||||
// Use the underlying GetAll which will force cache all domains
|
||||
=> GetMany().FirstOrDefault(x => x.Id == id);
|
||||
|
||||
protected override IEnumerable<IDomain> PerformGetAll(params int[]? ids)
|
||||
{
|
||||
@@ -49,12 +45,13 @@ internal class DomainRepository : EntityRepositoryBase<int, IDomain>, IDomainRep
|
||||
{
|
||||
sql.WhereIn<DomainDto>(x => x.Id, ids);
|
||||
}
|
||||
sql.OrderBy<DomainDto>(dto => dto.SortOrder);
|
||||
|
||||
return Database.Fetch<DomainDto>(sql).Select(ConvertFromDto);
|
||||
return Database.Fetch<DomainDto>(sql).Select(DomainFactory.BuildEntity);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IDomain> PerformGetByQuery(IQuery<IDomain> query) =>
|
||||
throw new NotSupportedException("This repository does not support this method");
|
||||
protected override IEnumerable<IDomain> PerformGetByQuery(IQuery<IDomain> query)
|
||||
=> throw new NotSupportedException("This repository does not support this method");
|
||||
|
||||
protected override Sql<ISqlContext> GetBaseQuery(bool isCount)
|
||||
{
|
||||
@@ -65,7 +62,7 @@ internal class DomainRepository : EntityRepositoryBase<int, IDomain>, IDomainRep
|
||||
}
|
||||
else
|
||||
{
|
||||
sql.Select("umbracoDomain.*, umbracoLanguage.languageISOCode")
|
||||
sql.Select($"{Constants.DatabaseSchema.Tables.Domain}.*, {Constants.DatabaseSchema.Tables.Language}.languageISOCode")
|
||||
.From<DomainDto>()
|
||||
.LeftJoin<LanguageDto>()
|
||||
.On<DomainDto, LanguageDto>(dto => dto.DefaultLanguage, dto => dto.Id);
|
||||
@@ -74,23 +71,23 @@ internal class DomainRepository : EntityRepositoryBase<int, IDomain>, IDomainRep
|
||||
return sql;
|
||||
}
|
||||
|
||||
protected override string GetBaseWhereClause() => $"{Constants.DatabaseSchema.Tables.Domain}.id = @id";
|
||||
protected override string GetBaseWhereClause()
|
||||
=> $"{Constants.DatabaseSchema.Tables.Domain}.id = @id";
|
||||
|
||||
protected override IEnumerable<string> GetDeleteClauses()
|
||||
=> new []
|
||||
{
|
||||
var list = new List<string> { "DELETE FROM umbracoDomain WHERE id = @id" };
|
||||
return list;
|
||||
}
|
||||
$"DELETE FROM {Constants.DatabaseSchema.Tables.Domain} WHERE id = @id",
|
||||
};
|
||||
|
||||
protected override void PersistNewItem(IDomain entity)
|
||||
{
|
||||
var exists = Database.ExecuteScalar<int>(
|
||||
"SELECT COUNT(*) FROM umbracoDomain WHERE domainName = @domainName",
|
||||
$"SELECT COUNT(*) FROM {Constants.DatabaseSchema.Tables.Domain} WHERE domainName = @domainName",
|
||||
new { domainName = entity.DomainName });
|
||||
if (exists > 0)
|
||||
{
|
||||
throw new DuplicateNameException(
|
||||
string.Format("The domain name {0} is already assigned", entity.DomainName));
|
||||
throw new DuplicateNameException($"The domain name {entity.DomainName} is already assigned.");
|
||||
}
|
||||
|
||||
if (entity.RootContentId.HasValue)
|
||||
@@ -100,34 +97,37 @@ internal class DomainRepository : EntityRepositoryBase<int, IDomain>, IDomainRep
|
||||
new { id = entity.RootContentId.Value });
|
||||
if (contentExists == 0)
|
||||
{
|
||||
throw new NullReferenceException("No content exists with id " + entity.RootContentId.Value);
|
||||
throw new NullReferenceException($"No content exists with id {entity.RootContentId.Value}.");
|
||||
}
|
||||
}
|
||||
|
||||
if (entity.LanguageId.HasValue)
|
||||
{
|
||||
var languageExists = Database.ExecuteScalar<int>(
|
||||
"SELECT COUNT(*) FROM umbracoLanguage WHERE id = @id",
|
||||
$"SELECT COUNT(*) FROM {Constants.DatabaseSchema.Tables.Language} WHERE id = @id",
|
||||
new { id = entity.LanguageId.Value });
|
||||
if (languageExists == 0)
|
||||
{
|
||||
throw new NullReferenceException("No language exists with id " + entity.LanguageId.Value);
|
||||
throw new NullReferenceException($"No language exists with id {entity.LanguageId.Value}.");
|
||||
}
|
||||
}
|
||||
|
||||
entity.AddingEntity();
|
||||
|
||||
var factory = new DomainModelFactory();
|
||||
DomainDto dto = factory.BuildDto(entity);
|
||||
// Get sort order
|
||||
entity.SortOrder = GetNewSortOrder(entity.RootContentId, entity.IsWildcard);
|
||||
|
||||
DomainDto dto = DomainFactory.BuildDto(entity);
|
||||
|
||||
var id = Convert.ToInt32(Database.Insert(dto));
|
||||
entity.Id = id;
|
||||
|
||||
// if the language changed, we need to resolve the ISO code!
|
||||
// If the language changed, we need to resolve the ISO code
|
||||
if (entity.LanguageId.HasValue)
|
||||
{
|
||||
((UmbracoDomain)entity).LanguageIsoCode = Database.ExecuteScalar<string>(
|
||||
"SELECT languageISOCode FROM umbracoLanguage WHERE id=@langId", new { langId = entity.LanguageId });
|
||||
$"SELECT languageISOCode FROM {Constants.DatabaseSchema.Tables.Language} WHERE id = @langId",
|
||||
new { langId = entity.LanguageId });
|
||||
}
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
@@ -137,15 +137,13 @@ internal class DomainRepository : EntityRepositoryBase<int, IDomain>, IDomainRep
|
||||
{
|
||||
entity.UpdatingEntity();
|
||||
|
||||
// Ensure there is no other domain with the same name on another entity
|
||||
var exists = Database.ExecuteScalar<int>(
|
||||
"SELECT COUNT(*) FROM umbracoDomain WHERE domainName = @domainName AND umbracoDomain.id <> @id",
|
||||
$"SELECT COUNT(*) FROM {Constants.DatabaseSchema.Tables.Domain} WHERE domainName = @domainName AND umbracoDomain.id <> @id",
|
||||
new { domainName = entity.DomainName, id = entity.Id });
|
||||
|
||||
// ensure there is no other domain with the same name on another entity
|
||||
if (exists > 0)
|
||||
{
|
||||
throw new DuplicateNameException(
|
||||
string.Format("The domain name {0} is already assigned", entity.DomainName));
|
||||
throw new DuplicateNameException($"The domain name {entity.DomainName} is already assigned.");
|
||||
}
|
||||
|
||||
if (entity.RootContentId.HasValue)
|
||||
@@ -155,69 +153,40 @@ internal class DomainRepository : EntityRepositoryBase<int, IDomain>, IDomainRep
|
||||
new { id = entity.RootContentId.Value });
|
||||
if (contentExists == 0)
|
||||
{
|
||||
throw new NullReferenceException("No content exists with id " + entity.RootContentId.Value);
|
||||
throw new NullReferenceException($"No content exists with id {entity.RootContentId.Value}.");
|
||||
}
|
||||
}
|
||||
|
||||
if (entity.LanguageId.HasValue)
|
||||
{
|
||||
var languageExists = Database.ExecuteScalar<int>(
|
||||
"SELECT COUNT(*) FROM umbracoLanguage WHERE id = @id",
|
||||
$"SELECT COUNT(*) FROM {Constants.DatabaseSchema.Tables.Language} WHERE id = @id",
|
||||
new { id = entity.LanguageId.Value });
|
||||
if (languageExists == 0)
|
||||
{
|
||||
throw new NullReferenceException("No language exists with id " + entity.LanguageId.Value);
|
||||
throw new NullReferenceException($"No language exists with id {entity.LanguageId.Value}.");
|
||||
}
|
||||
}
|
||||
|
||||
var factory = new DomainModelFactory();
|
||||
DomainDto dto = factory.BuildDto(entity);
|
||||
DomainDto dto = DomainFactory.BuildDto(entity);
|
||||
|
||||
Database.Update(dto);
|
||||
|
||||
// if the language changed, we need to resolve the ISO code!
|
||||
// If the language changed, we need to resolve the ISO code
|
||||
if (entity.WasPropertyDirty("LanguageId"))
|
||||
{
|
||||
((UmbracoDomain)entity).LanguageIsoCode = Database.ExecuteScalar<string>(
|
||||
"SELECT languageISOCode FROM umbracoLanguage WHERE id=@langId", new { langId = entity.LanguageId });
|
||||
$"SELECT languageISOCode FROM {Constants.DatabaseSchema.Tables.Language} WHERE id = @langId",
|
||||
new { langId = entity.LanguageId });
|
||||
}
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
}
|
||||
|
||||
private IDomain ConvertFromDto(DomainDto dto)
|
||||
{
|
||||
var factory = new DomainModelFactory();
|
||||
IDomain entity = factory.BuildEntity(dto);
|
||||
return entity;
|
||||
}
|
||||
|
||||
internal class DomainModelFactory
|
||||
{
|
||||
public IDomain BuildEntity(DomainDto dto)
|
||||
{
|
||||
var domain = new UmbracoDomain(dto.DomainName, dto.IsoCode)
|
||||
{
|
||||
Id = dto.Id,
|
||||
LanguageId = dto.DefaultLanguage,
|
||||
RootContentId = dto.RootStructureId,
|
||||
};
|
||||
|
||||
// reset dirty initial properties (U4-1946)
|
||||
domain.ResetDirtyProperties(false);
|
||||
return domain;
|
||||
}
|
||||
|
||||
public DomainDto BuildDto(IDomain entity)
|
||||
{
|
||||
var dto = new DomainDto
|
||||
{
|
||||
DefaultLanguage = entity.LanguageId,
|
||||
DomainName = entity.DomainName,
|
||||
Id = entity.Id,
|
||||
RootStructureId = entity.RootContentId,
|
||||
};
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
protected int GetNewSortOrder(int? rootContentId, bool isWildcard)
|
||||
=> isWildcard
|
||||
? -1
|
||||
: Database.ExecuteScalar<int>(
|
||||
$"SELECT COALESCE(MAX(sortOrder), -1) + 1 FROM {Constants.DatabaseSchema.Tables.Domain} WHERE domainRootStructureID = @rootContentId AND NOT (domainName = '' OR domainName LIKE '*%')",
|
||||
new { rootContentId });
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public class DomainCache : IDomainCache
|
||||
list = list.Where(x => x.IsWildcard == false);
|
||||
}
|
||||
|
||||
return list;
|
||||
return list.OrderBy(x => x.SortOrder);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -46,7 +46,7 @@ public class DomainCache : IDomainCache
|
||||
list = list.Where(x => x.IsWildcard == false);
|
||||
}
|
||||
|
||||
return list;
|
||||
return list.OrderBy(x => x.SortOrder);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -298,15 +298,15 @@ internal class PublishedSnapshotService : IPublishedSnapshotService
|
||||
continue; // anomaly
|
||||
}
|
||||
|
||||
if (domain.LanguageIsoCode.IsNullOrWhiteSpace())
|
||||
var culture = domain.LanguageIsoCode;
|
||||
if (string.IsNullOrWhiteSpace(culture))
|
||||
{
|
||||
continue; // anomaly
|
||||
}
|
||||
|
||||
var culture = domain.LanguageIsoCode;
|
||||
_domainStore.SetLocked(
|
||||
domain.Id,
|
||||
new Domain(domain.Id, domain.DomainName, domain.RootContentId.Value, culture, domain.IsWildcard));
|
||||
new Domain(domain.Id, domain.DomainName, domain.RootContentId.Value, culture, domain.IsWildcard, domain.SortOrder));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -832,7 +832,7 @@ internal class PublishedSnapshotService : IPublishedSnapshotService
|
||||
{
|
||||
foreach (Domain domain in domains
|
||||
.Where(x => x.RootContentId.HasValue && x.LanguageIsoCode.IsNullOrWhiteSpace() == false)
|
||||
.Select(x => new Domain(x.Id, x.DomainName, x.RootContentId!.Value, x.LanguageIsoCode!, x.IsWildcard)))
|
||||
.Select(x => new Domain(x.Id, x.DomainName, x.RootContentId!.Value, x.LanguageIsoCode!, x.IsWildcard, x.SortOrder)))
|
||||
{
|
||||
_domainStore.SetLocked(domain.Id, domain);
|
||||
}
|
||||
|
||||
@@ -2246,16 +2246,14 @@ public class ContentController : ContentControllerBase
|
||||
|
||||
public ContentDomainsAndCulture GetCultureAndDomains(int id)
|
||||
{
|
||||
IDomain[]? nodeDomains = _domainService.GetAssignedDomains(id, true)?.ToArray();
|
||||
IDomain? wildcard = nodeDomains?.FirstOrDefault(d => d.IsWildcard);
|
||||
IEnumerable<DomainDisplay>? domains = nodeDomains?.Where(d => !d.IsWildcard)
|
||||
.Select(d => new DomainDisplay(d.DomainName, d.LanguageId.GetValueOrDefault(0)));
|
||||
IDomain[] assignedDomains = _domainService.GetAssignedDomains(id, true).ToArray();
|
||||
IDomain? wildcard = assignedDomains.FirstOrDefault(d => d.IsWildcard);
|
||||
IEnumerable<DomainDisplay> domains = assignedDomains.Where(d => !d.IsWildcard).Select(d => new DomainDisplay(d.DomainName, d.LanguageId.GetValueOrDefault(0)));
|
||||
|
||||
return new ContentDomainsAndCulture
|
||||
{
|
||||
Language = wildcard == null || !wildcard.LanguageId.HasValue ? "undefined" : wildcard.LanguageId.ToString(),
|
||||
Domains = domains,
|
||||
Language = wildcard == null || !wildcard.LanguageId.HasValue
|
||||
? "undefined"
|
||||
: wildcard.LanguageId.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2264,11 +2262,11 @@ public class ContentController : ContentControllerBase
|
||||
{
|
||||
if (model.Domains is not null)
|
||||
{
|
||||
foreach (DomainDisplay domain in model.Domains)
|
||||
foreach (DomainDisplay domainDisplay in model.Domains)
|
||||
{
|
||||
try
|
||||
{
|
||||
Uri uri = DomainUtilities.ParseUriFromDomainName(domain.Name, new Uri(Request.GetEncodedUrl()));
|
||||
DomainUtilities.ParseUriFromDomainName(domainDisplay.Name, new Uri(Request.GetEncodedUrl()));
|
||||
}
|
||||
catch (UriFormatException)
|
||||
{
|
||||
@@ -2277,18 +2275,16 @@ public class ContentController : ContentControllerBase
|
||||
}
|
||||
}
|
||||
|
||||
// Validate node
|
||||
IContent? node = _contentService.GetById(model.NodeId);
|
||||
|
||||
if (node == null)
|
||||
{
|
||||
HttpContext.SetReasonPhrase("Node Not Found.");
|
||||
return NotFound("There is no content node with id {model.NodeId}.");
|
||||
}
|
||||
|
||||
EntityPermission? permission =
|
||||
_userService.GetPermissions(_backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser, node.Path);
|
||||
|
||||
|
||||
// Validate permissions on node
|
||||
EntityPermission? permission = _userService.GetPermissions(_backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser, node.Path);
|
||||
if (permission?.AssignedPermissions.Contains(ActionAssignDomain.ActionLetter.ToString(), StringComparer.Ordinal) == false)
|
||||
{
|
||||
HttpContext.SetReasonPhrase("Permission Denied.");
|
||||
@@ -2296,120 +2292,118 @@ public class ContentController : ContentControllerBase
|
||||
}
|
||||
|
||||
model.Valid = true;
|
||||
IDomain[]? domains = _domainService.GetAssignedDomains(model.NodeId, true)?.ToArray();
|
||||
ILanguage[] languages = _localizationService.GetAllLanguages().ToArray();
|
||||
ILanguage? language = model.Language > 0 ? languages.FirstOrDefault(l => l.Id == model.Language) : null;
|
||||
|
||||
// process wildcard
|
||||
if (language != null)
|
||||
IDomain[] assignedDomains = _domainService.GetAssignedDomains(model.NodeId, true).ToArray();
|
||||
ILanguage[] languages = _localizationService.GetAllLanguages().ToArray();
|
||||
|
||||
// Process language
|
||||
ILanguage? language = model.Language > 0 ? languages.FirstOrDefault(l => l.Id == model.Language) : null;
|
||||
if (language is not null)
|
||||
{
|
||||
// yet there is a race condition here...
|
||||
IDomain? wildcard = domains?.FirstOrDefault(d => d.IsWildcard);
|
||||
if (wildcard != null)
|
||||
// Update or create language on wildcard domain
|
||||
IDomain? assignedWildcardDomain = assignedDomains.FirstOrDefault(d => d.IsWildcard);
|
||||
if (assignedWildcardDomain is not null)
|
||||
{
|
||||
wildcard.LanguageId = language.Id;
|
||||
assignedWildcardDomain.LanguageId = language.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
wildcard = new UmbracoDomain("*" + model.NodeId)
|
||||
assignedWildcardDomain = new UmbracoDomain("*" + model.NodeId)
|
||||
{
|
||||
LanguageId = model.Language,
|
||||
RootContentId = model.NodeId
|
||||
};
|
||||
}
|
||||
|
||||
Attempt<OperationResult?> saveAttempt = _domainService.Save(wildcard);
|
||||
if (saveAttempt == false)
|
||||
Attempt<OperationResult?> saveAttempt = _domainService.Save(assignedWildcardDomain);
|
||||
if (saveAttempt.Success == false)
|
||||
{
|
||||
HttpContext.SetReasonPhrase(saveAttempt.Result?.Result.ToString());
|
||||
return BadRequest("Saving domain failed");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
// Delete every domain that's in the database, but not in the model
|
||||
foreach (IDomain? assignedDomain in assignedDomains.Where(d => (d.IsWildcard && language is null) || (d.IsWildcard == false && (model.Domains is null || model.Domains.All(m => m.Name.InvariantEquals(d.DomainName) == false)))))
|
||||
{
|
||||
IDomain? wildcard = domains?.FirstOrDefault(d => d.IsWildcard);
|
||||
if (wildcard != null)
|
||||
{
|
||||
_domainService.Delete(wildcard);
|
||||
}
|
||||
_domainService.Delete(assignedDomain);
|
||||
}
|
||||
|
||||
// process domains
|
||||
// delete every (non-wildcard) domain, that exists in the DB yet is not in the model
|
||||
foreach (IDomain domain in domains?.Where(d =>
|
||||
d.IsWildcard == false &&
|
||||
(model.Domains?.All(m => m.Name.InvariantEquals(d.DomainName) == false) ??
|
||||
false)) ??
|
||||
Array.Empty<IDomain>())
|
||||
// Process domains
|
||||
if (model.Domains is not null)
|
||||
{
|
||||
_domainService.Delete(domain);
|
||||
}
|
||||
|
||||
var names = new List<string>();
|
||||
|
||||
// create or update domains in the model
|
||||
foreach (DomainDisplay domainModel in model.Domains?.Where(m => string.IsNullOrWhiteSpace(m.Name) == false) ??
|
||||
Array.Empty<DomainDisplay>())
|
||||
var savedDomains = new List<IDomain>();
|
||||
foreach (DomainDisplay domainDisplay in model.Domains.Where(m => string.IsNullOrWhiteSpace(m.Name) == false))
|
||||
{
|
||||
language = languages.FirstOrDefault(l => l.Id == domainModel.Lang);
|
||||
language = languages.FirstOrDefault(l => l.Id == domainDisplay.Lang);
|
||||
if (language == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var name = domainModel.Name.ToLowerInvariant();
|
||||
if (names.Contains(name))
|
||||
var domainName = domainDisplay.Name.ToLowerInvariant();
|
||||
if (savedDomains.Any(d => d.DomainName == domainName))
|
||||
{
|
||||
domainModel.Duplicate = true;
|
||||
domainDisplay.Duplicate = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
names.Add(name);
|
||||
IDomain? domain = domains?.FirstOrDefault(d => d.DomainName.InvariantEquals(domainModel.Name));
|
||||
IDomain? domain = assignedDomains.FirstOrDefault(d => d.DomainName.InvariantEquals(domainName));
|
||||
if (domain is null && _domainService.GetByName(domainName) is IDomain existingDomain)
|
||||
{
|
||||
// Domain name already exists on another node
|
||||
domainDisplay.Duplicate = true;
|
||||
|
||||
// Add node breadcrumbs
|
||||
if (existingDomain.RootContentId is int rootContentId)
|
||||
{
|
||||
var breadcrumbs = new List<string?>();
|
||||
|
||||
IContent? content = _contentService.GetById(rootContentId);
|
||||
while (content is not null)
|
||||
{
|
||||
breadcrumbs.Add(content.Name);
|
||||
if (content.ParentId < -1)
|
||||
{
|
||||
breadcrumbs.Add("Recycle Bin");
|
||||
}
|
||||
|
||||
content = _contentService.GetParent(content);
|
||||
}
|
||||
|
||||
breadcrumbs.Reverse();
|
||||
domainDisplay.Other = "/" + string.Join("/", breadcrumbs);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update or create domain
|
||||
if (domain != null)
|
||||
{
|
||||
domain.LanguageId = language.Id;
|
||||
_domainService.Save(domain);
|
||||
}
|
||||
else if (_domainService.Exists(domainModel.Name))
|
||||
{
|
||||
domainModel.Duplicate = true;
|
||||
IDomain? xdomain = _domainService.GetByName(domainModel.Name);
|
||||
var xrcid = xdomain?.RootContentId;
|
||||
if (xrcid.HasValue)
|
||||
{
|
||||
IContent? xcontent = _contentService.GetById(xrcid.Value);
|
||||
var xnames = new List<string>();
|
||||
while (xcontent != null)
|
||||
{
|
||||
if (xcontent.Name is not null)
|
||||
{
|
||||
xnames.Add(xcontent.Name);
|
||||
}
|
||||
|
||||
if (xcontent.ParentId < -1)
|
||||
{
|
||||
xnames.Add("Recycle Bin");
|
||||
}
|
||||
|
||||
xcontent = _contentService.GetParent(xcontent);
|
||||
}
|
||||
|
||||
xnames.Reverse();
|
||||
domainModel.Other = "/" + string.Join("/", xnames);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// yet there is a race condition here...
|
||||
var newDomain = new UmbracoDomain(name) { LanguageId = domainModel.Lang, RootContentId = model.NodeId };
|
||||
Attempt<OperationResult?> saveAttempt = _domainService.Save(newDomain);
|
||||
if (saveAttempt == false)
|
||||
domain = new UmbracoDomain(domainName)
|
||||
{
|
||||
LanguageId = language.Id,
|
||||
RootContentId = model.NodeId,
|
||||
};
|
||||
}
|
||||
|
||||
Attempt<OperationResult?> saveAttempt = _domainService.Save(domain);
|
||||
if (saveAttempt.Success == false)
|
||||
{
|
||||
HttpContext.SetReasonPhrase(saveAttempt.Result?.Result.ToString());
|
||||
return BadRequest("Saving new domain failed");
|
||||
return BadRequest("Saving domain failed");
|
||||
}
|
||||
|
||||
savedDomains.Add(domain);
|
||||
}
|
||||
|
||||
// Sort saved domains
|
||||
_domainService.Sort(savedDomains);
|
||||
}
|
||||
|
||||
model.Valid = model.Domains?.All(m => m.Duplicate == false) ?? false;
|
||||
|
||||
@@ -88,14 +88,42 @@ Use this directive to render a button with a dropdown of alternative actions.
|
||||
@param {string=} direction Set the direction of the dropdown ("up", "down").
|
||||
@param {string=} float Set the float of the dropdown. ("left", "right").
|
||||
**/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function ButtonGroupDirective() {
|
||||
|
||||
function link(scope) {
|
||||
function controller($scope) {
|
||||
$scope.toggleStyle = null;
|
||||
$scope.blockElement = false;
|
||||
|
||||
var buttonStyle = $scope.buttonStyle;
|
||||
if (buttonStyle) {
|
||||
// Make it possible to pass in multiple styles
|
||||
if (buttonStyle.startsWith("[") && buttonStyle.endsWith("]")) {
|
||||
// when using an attr it will always be a string so we need to remove square brackets and turn it into and array
|
||||
var withoutBrackets = buttonStyle.replace(/[\[\]']+/g, '');
|
||||
// split array by , + make sure to catch whitespaces
|
||||
var array = withoutBrackets.split(/\s?,\s?/g);
|
||||
|
||||
Utilities.forEach(array, item => {
|
||||
if (item === "block") {
|
||||
$scope.blockElement = true;
|
||||
} else {
|
||||
$scope.toggleStyle = ($scope.toggleStyle ? $scope.toggleStyle + " " : "") + "btn-" + item;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (buttonStyle === "block") {
|
||||
$scope.blockElement = true;
|
||||
} else {
|
||||
$scope.toggleStyle = "btn-" + buttonStyle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function link(scope) {
|
||||
scope.dropdown = {
|
||||
isOpen: false
|
||||
};
|
||||
@@ -112,13 +140,13 @@ Use this directive to render a button with a dropdown of alternative actions.
|
||||
subButton.handler();
|
||||
scope.closeDropdown();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
templateUrl: 'views/components/buttons/umb-button-group.html',
|
||||
controller: controller,
|
||||
scope: {
|
||||
defaultButton: "=",
|
||||
subButtons: "=",
|
||||
@@ -139,5 +167,4 @@ Use this directive to render a button with a dropdown of alternative actions.
|
||||
}
|
||||
|
||||
angular.module('umbraco.directives').directive('umbButtonGroup', ButtonGroupDirective);
|
||||
|
||||
})();
|
||||
|
||||
@@ -198,3 +198,19 @@
|
||||
.btn-group-vertical > .btn-large:last-child {
|
||||
.border-radius(0 0 @borderRadiusLarge @borderRadiusLarge);
|
||||
}
|
||||
|
||||
.btn-group-justified {
|
||||
display: flex;
|
||||
|
||||
.umb-button {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
> * {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
> .dropdown-toggle {
|
||||
flex-grow: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -577,11 +577,6 @@ div.help {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
|
||||
table.domains .help-inline {
|
||||
color:@red;
|
||||
}
|
||||
|
||||
// INPUT GROUPS
|
||||
// ------------
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="btn-group umb-button-group" ng-class="{ 'dropup': direction === 'up', '-with-button-group-toggle': subButtons.length > 0 }">
|
||||
<div class="btn-group umb-button-group" ng-class="{ 'dropup': direction === 'up', '-with-button-group-toggle': subButtons.length > 0, 'btn-group-justified': blockElement }">
|
||||
<umb-button
|
||||
ng-if="defaultButton"
|
||||
alias="{{defaultButton.alias ? defaultButton.alias : 'groupPrimary' }}"
|
||||
@@ -21,7 +21,7 @@
|
||||
<button
|
||||
type="button"
|
||||
data-element="button-group-toggle"
|
||||
class="btn btn-{{buttonStyle}} dropdown-toggle umb-button-group__toggle umb-button--{{size}}"
|
||||
class="btn {{toggleStyle}} dropdown-toggle umb-button-group__toggle {{size ? 'umb-button--' + size : undefined}}"
|
||||
ng-if="subButtons.length > 0"
|
||||
ng-click="toggleDropdown()"
|
||||
aria-haspopup="true"
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
<div ng-controller="Umbraco.Editors.Content.AssignDomainController as vm" ng-cloak>
|
||||
|
||||
<umb-load-indicator ng-show="vm.loading"></umb-load-indicator>
|
||||
|
||||
<form name="vm.domainForm" ng-submit="vm.save()" id="assignDomain" novalidate>
|
||||
|
||||
<div ng-hide="vm.loading" class="umb-dialog-body">
|
||||
|
||||
<umb-pane ng-if="!currentNode.metaData.variesByCulture">
|
||||
<h5 class="umb-pane-title"><localize key="assignDomain_setLanguage">Culture</localize></h5>
|
||||
<label for="assignDomain_language" class="control-label"><localize key="general_language">Language</localize></label>
|
||||
<select class="umb-property-editor umb-dropdown" name="language" id="assignDomain_language" ng-model="vm.language" ng-options="lang.name for lang in vm.languages">
|
||||
<option value="">{{vm.inherit}}</option>
|
||||
<option value=""><localize key="assignDomain_inherit">Inherit</localize></option>
|
||||
</select>
|
||||
</umb-pane>
|
||||
|
||||
<umb-pane>
|
||||
|
||||
<div ng-show="vm.error">
|
||||
<div class="alert alert-error">
|
||||
<div><strong>{{vm.error.errorMsg}}</strong></div>
|
||||
@@ -24,94 +20,48 @@
|
||||
</div>
|
||||
|
||||
<h5 class="umb-pane-title"><localize key="assignDomain_setDomains">Domains</localize></h5>
|
||||
<small class="db mb3">
|
||||
<localize key="assignDomain_domainHelpWithVariants">Valid domain names are: "example.com", "www.example.com", "example.com:8080", or "https://www.example.com/".
|
||||
Furthermore also one-level paths in domains are supported, eg. "example.com/en" or "/en".</localize>
|
||||
</small>
|
||||
<div class="umb-el-wrap hidelabel">
|
||||
<table class="table table-condensed table-bordered domains mb3" ng-if="vm.domains.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<localize key="assignDomain_domain">Domain</localize>
|
||||
<span class="umb-control-required">*</span>
|
||||
</th>
|
||||
<th>
|
||||
<localize key="assignDomain_language">Language</localize>
|
||||
<span class="umb-control-required">*</span>
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="domain in vm.domains">
|
||||
<td>
|
||||
<p><small><localize key="assignDomain_domainHelpWithVariants">Valid domain names are: "example.com", "www.example.com", "example.com:8080", or "https://www.example.com/". Furthermore also one-level paths in domains are supported, eg. "example.com/en" or "/en".</localize></small></p>
|
||||
|
||||
<input type="text" class="w-100" ng-model="domain.name" name="domain_name_{{$index}}" required umb-auto-focus />
|
||||
|
||||
<span ng-if="vm.domainForm.$submitted" ng-messages="vm.domainForm['domain_name_' + $index].$error">
|
||||
<span class="help-inline" ng-message="required"><localize key="validation_invalidEmpty">Value cannot be empty</localize></span>
|
||||
</span>
|
||||
<span ng-show="domain.duplicate" class="help-inline"><localize key="assignDomain_duplicateDomain">Domain has already been assigned.</localize><span ng-show="domain.other">({{domain.other}})</span></span>
|
||||
</td>
|
||||
<td>
|
||||
<select
|
||||
name="domain_language_{{$index}}"
|
||||
class="language w-100"
|
||||
ng-model="domain.lang"
|
||||
ng-options="lang.name for lang in vm.languages"
|
||||
required>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<umb-button
|
||||
icon="icon-trash"
|
||||
action="vm.removeDomain($index)"
|
||||
type="button"
|
||||
button-style="danger">
|
||||
</umb-button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="form-horizontal" ui-sortable="sortableOptions" ng-model="vm.domains">
|
||||
<div ng-repeat="domain in vm.domains">
|
||||
<div ng-if="domain.duplicate" class="alert alert-error property-error">
|
||||
<localize key="assignDomain_duplicateDomain">Domain has already been assigned.</localize><span ng-if="domain.other"> ({{domain.other}})</span>
|
||||
</div>
|
||||
|
||||
<umb-button
|
||||
label-key="assignDomain_addNew"
|
||||
action="vm.addDomain()"
|
||||
type="button"
|
||||
button-style="info">
|
||||
</umb-button>
|
||||
|
||||
<umb-button
|
||||
label-key="assignDomain_addCurrent"
|
||||
action="vm.addCurrentDomain()"
|
||||
type="button"
|
||||
button-style="success">
|
||||
</umb-button>
|
||||
|
||||
<div ng-if="vm.domainForm.$submitted" ng-messages="vm.domainForm['domain_name_' + $index].$error">
|
||||
<div class="alert alert-error property-error" ng-message="required"><localize key="validation_invalidEmpty">Value cannot be empty</localize></div>
|
||||
</div>
|
||||
<div class="umb-prevalues-multivalues__listitem">
|
||||
<umb-icon icon="icon-navigation" class="icon handle"></umb-icon>
|
||||
<div class="umb-prevalues-multivalues__left">
|
||||
<input type="text" ng-model="domain.name" name="domain_name_{{$index}}" required umb-auto-focus />
|
||||
<select ng-model="domain.lang" name="domain_language_{{$index}}" ng-options="lang.name for lang in vm.languages" required></select>
|
||||
</div>
|
||||
<div class="umb-prevalues-multivalues__right">
|
||||
<button type="button" class="umb-node-preview__action" ng-click="vm.removeDomain($index)"><localize key="general_remove">Remove</localize></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<umb-button-group ng-if="vm.buttonGroup"
|
||||
default-button="vm.buttonGroup.defaultButton"
|
||||
sub-buttons="vm.buttonGroup.subButtons"
|
||||
button-style="[placeholder, block]"
|
||||
float="right">
|
||||
</umb-button-group>
|
||||
</umb-pane>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
|
||||
<umb-button
|
||||
label-key="general_close"
|
||||
<umb-button label-key="general_close"
|
||||
action="vm.closeDialog()"
|
||||
type="button"
|
||||
button-style="link">
|
||||
</umb-button>
|
||||
|
||||
<umb-button
|
||||
label-key="buttons_save"
|
||||
<umb-button label-key="buttons_save"
|
||||
type="submit"
|
||||
button-style="success"
|
||||
state="vm.submitButtonState">
|
||||
</umb-button>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -4,19 +4,38 @@
|
||||
function AssignDomainController($scope, localizationService, languageResource, contentResource, navigationService, notificationsService, $location) {
|
||||
var vm = this;
|
||||
|
||||
vm.loading = true;
|
||||
vm.closeDialog = closeDialog;
|
||||
vm.addDomain = addDomain;
|
||||
vm.addCurrentDomain = addCurrentDomain;
|
||||
vm.removeDomain = removeDomain;
|
||||
vm.save = save;
|
||||
vm.languages = [];
|
||||
vm.domains = [];
|
||||
vm.language = null;
|
||||
|
||||
vm.buttonGroup = {
|
||||
defaultButton: {
|
||||
labelKey: 'assignDomain_addNew',
|
||||
buttonStyle: 'info',
|
||||
handler: addDomain
|
||||
},
|
||||
subButtons: [{
|
||||
labelKey: 'assignDomain_addCurrent',
|
||||
buttonStyle: 'success',
|
||||
handler: addCurrentDomain
|
||||
}]
|
||||
};
|
||||
|
||||
$scope.sortableOptions = {
|
||||
axis: 'y',
|
||||
containment: 'parent',
|
||||
cursor: 'move',
|
||||
handle: ".handle",
|
||||
placeholder: 'sortable-placeholder',
|
||||
forcePlaceholderSize: true,
|
||||
tolerance: 'pointer'
|
||||
};
|
||||
|
||||
function activate() {
|
||||
|
||||
vm.loading = true;
|
||||
|
||||
languageResource.getAll().then(langs => {
|
||||
vm.languages = langs;
|
||||
|
||||
@@ -30,25 +49,13 @@
|
||||
else {
|
||||
vm.defaultLanguage = langs[0];
|
||||
}
|
||||
getCultureAndDomains().then(() => {
|
||||
vm.loading = false;
|
||||
});
|
||||
});
|
||||
|
||||
localizationService.localize("assignDomain_inherit").then(function (value) {
|
||||
vm.inherit = value;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function getCultureAndDomains () {
|
||||
return contentResource.getCultureAndDomains($scope.currentNode.id)
|
||||
.then(function (data) {
|
||||
|
||||
contentResource.getCultureAndDomains($scope.currentNode.id).then(function (data) {
|
||||
if (data.language !== "undefined") {
|
||||
var lang = vm.languages.filter(function (l) {
|
||||
return matchLanguageById(l, data.language);
|
||||
});
|
||||
|
||||
if (lang.length > 0) {
|
||||
vm.language = lang[0];
|
||||
}
|
||||
@@ -58,17 +65,22 @@
|
||||
var matchedLangs = vm.languages.filter(function (lng) {
|
||||
return matchLanguageById(lng, d.lang);
|
||||
});
|
||||
|
||||
return {
|
||||
name: d.name,
|
||||
lang: matchedLangs.length > 0 ? matchedLangs[0] : vm.defaultLanguage
|
||||
}
|
||||
});
|
||||
|
||||
vm.loading = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function matchLanguageById(language, id) {
|
||||
var langId = parseInt(language.id);
|
||||
var comparisonId = parseInt(id);
|
||||
|
||||
return langId === comparisonId;
|
||||
}
|
||||
|
||||
@@ -89,6 +101,7 @@
|
||||
if (port != 80 && port != 443) {
|
||||
domainToAdd += ":" + port;
|
||||
}
|
||||
|
||||
vm.domains.push({
|
||||
name: domainToAdd,
|
||||
lang: vm.defaultLanguage
|
||||
@@ -100,12 +113,10 @@
|
||||
}
|
||||
|
||||
function save() {
|
||||
|
||||
vm.error = null;
|
||||
vm.submitButtonState = "busy";
|
||||
|
||||
if (vm.domainForm.$valid) {
|
||||
|
||||
// clear validation messages
|
||||
vm.domains.forEach(domain => {
|
||||
domain.duplicate = null;
|
||||
@@ -124,17 +135,17 @@
|
||||
};
|
||||
|
||||
contentResource.saveLanguageAndDomains(data).then(function (response) {
|
||||
|
||||
// validation is interesting. Check if response is valid
|
||||
if (response.valid) {
|
||||
vm.submitButtonState = "success";
|
||||
|
||||
localizationService.localize('speechBubbles_editCulturesAndHostnamesSaved').then(function (value) {
|
||||
notificationsService.success(value);
|
||||
});
|
||||
closeDialog();
|
||||
|
||||
// show validation messages for each domain
|
||||
closeDialog();
|
||||
} else {
|
||||
// show validation messages for each domain
|
||||
response.domains.forEach(validation => {
|
||||
vm.domains.forEach(domain => {
|
||||
if (validation.name === domain.name) {
|
||||
@@ -143,24 +154,25 @@
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
vm.submitButtonState = "error";
|
||||
|
||||
localizationService.localize('speechBubbles_editCulturesAndHostnamesError').then(function (value) {
|
||||
notificationsService.error(value);
|
||||
});
|
||||
}
|
||||
|
||||
}, function (e) {
|
||||
vm.error = e;
|
||||
vm.submitButtonState = "error";
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
vm.submitButtonState = "error";
|
||||
}
|
||||
}
|
||||
|
||||
activate();
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.Content.AssignDomainController", AssignDomainController);
|
||||
})();
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ public class ContentFinderByUrlTests : PublishedSnapshotServiceTestBase
|
||||
|
||||
var (finder, frequest) = await GetContentFinder(urlString);
|
||||
|
||||
frequest.SetDomain(new DomainAndUri(new Domain(1, "mysite", -1, "en-US", false), new Uri("http://mysite/")));
|
||||
frequest.SetDomain(new DomainAndUri(new Domain(1, "mysite", -1, "en-US", false, 0), new Uri("http://mysite/")));
|
||||
|
||||
var result = await finder.TryFindContent(frequest);
|
||||
|
||||
@@ -155,7 +155,7 @@ public class ContentFinderByUrlTests : PublishedSnapshotServiceTestBase
|
||||
|
||||
var (finder, frequest) = await GetContentFinder(urlString);
|
||||
|
||||
frequest.SetDomain(new DomainAndUri(new Domain(1, "mysite/æøå", -1, "en-US", false), new Uri("http://mysite/æøå")));
|
||||
frequest.SetDomain(new DomainAndUri(new Domain(1, "mysite/æøå", -1, "en-US", false, 0), new Uri("http://mysite/æøå")));
|
||||
|
||||
var result = await finder.TryFindContent(frequest);
|
||||
|
||||
|
||||
@@ -192,8 +192,8 @@ public class SiteDomainMapperTests
|
||||
var current = new Uri("https://domain1.com/foo/bar");
|
||||
Domain[] domains =
|
||||
{
|
||||
new Domain(1, "domain2.com", -1, s_cultureFr, false),
|
||||
new Domain(1, "domain1.com", -1, s_cultureGb, false),
|
||||
new Domain(1, "domain2.com", -1, s_cultureFr, false, 0),
|
||||
new Domain(1, "domain1.com", -1, s_cultureGb, false, 1),
|
||||
};
|
||||
var domainAndUris = DomainAndUris(current, domains);
|
||||
var output = siteDomainMapper.MapDomain(domainAndUris, current, s_cultureFr, s_cultureFr).Uri.ToString();
|
||||
@@ -203,8 +203,8 @@ public class SiteDomainMapperTests
|
||||
current = new Uri("https://domain1.com/foo/bar");
|
||||
domains = new[]
|
||||
{
|
||||
new Domain(1, "https://domain1.com", -1, s_cultureFr, false),
|
||||
new Domain(1, "https://domain2.com", -1, s_cultureGb, false),
|
||||
new Domain(1, "https://domain1.com", -1, s_cultureFr, false, 0),
|
||||
new Domain(1, "https://domain2.com", -1, s_cultureGb, false, 1),
|
||||
};
|
||||
domainAndUris = DomainAndUris(current, domains);
|
||||
output = siteDomainMapper.MapDomain(domainAndUris, current, s_cultureFr, s_cultureFr).Uri.ToString();
|
||||
@@ -213,8 +213,8 @@ public class SiteDomainMapperTests
|
||||
current = new Uri("https://domain1.com/foo/bar");
|
||||
domains = new[]
|
||||
{
|
||||
new Domain(1, "https://domain1.com", -1, s_cultureFr, false),
|
||||
new Domain(1, "https://domain4.com", -1, s_cultureGb, false),
|
||||
new Domain(1, "https://domain1.com", -1, s_cultureFr, false, 0),
|
||||
new Domain(1, "https://domain4.com", -1, s_cultureGb, false, 1),
|
||||
};
|
||||
domainAndUris = DomainAndUris(current, domains);
|
||||
output = siteDomainMapper.MapDomain(domainAndUris, current, s_cultureFr, s_cultureFr).Uri.ToString();
|
||||
@@ -223,8 +223,8 @@ public class SiteDomainMapperTests
|
||||
current = new Uri("https://domain4.com/foo/bar");
|
||||
domains = new[]
|
||||
{
|
||||
new Domain(1, "https://domain1.com", -1, s_cultureFr, false),
|
||||
new Domain(1, "https://domain4.com", -1, s_cultureGb, false),
|
||||
new Domain(1, "https://domain1.com", -1, s_cultureFr, false, 0),
|
||||
new Domain(1, "https://domain4.com", -1, s_cultureGb, false, 1),
|
||||
};
|
||||
domainAndUris = DomainAndUris(current, domains);
|
||||
output = siteDomainMapper.MapDomain(domainAndUris, current, s_cultureFr, s_cultureFr).Uri.ToString();
|
||||
@@ -247,8 +247,8 @@ public class SiteDomainMapperTests
|
||||
var output = siteDomainMapper.MapDomain(
|
||||
new[]
|
||||
{
|
||||
new DomainAndUri(new Domain(1, "domain1.com", -1, s_cultureFr, false), current),
|
||||
new DomainAndUri(new Domain(1, "domain2.com", -1, s_cultureGb, false), current),
|
||||
new DomainAndUri(new Domain(1, "domain1.com", -1, s_cultureFr, false, 0), current),
|
||||
new DomainAndUri(new Domain(1, "domain2.com", -1, s_cultureGb, false, 1), current),
|
||||
},
|
||||
current,
|
||||
s_cultureFr,
|
||||
@@ -261,8 +261,8 @@ public class SiteDomainMapperTests
|
||||
output = siteDomainMapper.MapDomain(
|
||||
new[]
|
||||
{
|
||||
new DomainAndUri(new Domain(1, "domain1.net", -1, s_cultureFr, false), current),
|
||||
new DomainAndUri(new Domain(1, "domain2.net", -1, s_cultureGb, false), current),
|
||||
new DomainAndUri(new Domain(1, "domain1.net", -1, s_cultureFr, false, 0), current),
|
||||
new DomainAndUri(new Domain(1, "domain2.net", -1, s_cultureGb, false, 1), current),
|
||||
},
|
||||
current,
|
||||
s_cultureFr,
|
||||
@@ -276,8 +276,8 @@ public class SiteDomainMapperTests
|
||||
output = siteDomainMapper.MapDomain(
|
||||
new[]
|
||||
{
|
||||
new DomainAndUri(new Domain(1, "domain2.net", -1, s_cultureFr, false), current),
|
||||
new DomainAndUri(new Domain(1, "domain1.net", -1, s_cultureGb, false), current),
|
||||
new DomainAndUri(new Domain(1, "domain2.net", -1, s_cultureFr, false, 0), current),
|
||||
new DomainAndUri(new Domain(1, "domain1.net", -1, s_cultureGb, false, 1), current),
|
||||
},
|
||||
current,
|
||||
s_cultureFr,
|
||||
@@ -305,38 +305,38 @@ public class SiteDomainMapperTests
|
||||
var output = siteDomainMapper.MapDomains(
|
||||
new[]
|
||||
{
|
||||
new DomainAndUri(new Domain(1, "domain1.com", -1, s_cultureFr, false), current), // no: current + what MapDomain would pick
|
||||
new DomainAndUri(new Domain(1, "domain2.com", -1, s_cultureGb, false), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain3.com", -1, s_cultureGb, false), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain4.com", -1, s_cultureGb, false), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain1.org", -1, s_cultureGb, false), current), // yes: same site (though bogus setup)
|
||||
new DomainAndUri(new Domain(1, "domain1.com", -1, s_cultureFr, false, 0), current), // no: current + what MapDomain would pick
|
||||
new DomainAndUri(new Domain(1, "domain2.com", -1, s_cultureGb, false, 1), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain3.com", -1, s_cultureGb, false, 2), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain4.com", -1, s_cultureGb, false, 3), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain1.org", -1, s_cultureGb, false, 4), current), // yes: same site (though bogus setup)
|
||||
},
|
||||
current,
|
||||
true,
|
||||
s_cultureFr,
|
||||
s_cultureFr).ToArray();
|
||||
|
||||
Assert.AreEqual(1, output.Count());
|
||||
Assert.Contains("http://domain1.org/", output.Select(d => d.Uri.ToString()).ToArray());
|
||||
Assert.AreEqual(1, output.Length);
|
||||
Assert.AreEqual("http://domain1.org/", output[0].Uri.ToString());
|
||||
|
||||
// current is a site1 uri, domains does not contain current
|
||||
current = new Uri("http://domain1.com/foo/bar");
|
||||
output = siteDomainMapper.MapDomains(
|
||||
new[]
|
||||
{
|
||||
new DomainAndUri(new Domain(1, "domain1.net", -1, s_cultureFr, false), current), // no: what MapDomain would pick
|
||||
new DomainAndUri(new Domain(1, "domain2.com", -1, s_cultureGb, false), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain3.com", -1, s_cultureGb, false), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain4.com", -1, s_cultureGb, false), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain1.org", -1, s_cultureGb, false), current), // yes: same site (though bogus setup)
|
||||
new DomainAndUri(new Domain(1, "domain1.net", -1, s_cultureFr, false, 0), current), // no: what MapDomain would pick
|
||||
new DomainAndUri(new Domain(1, "domain2.com", -1, s_cultureGb, false, 1), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain3.com", -1, s_cultureGb, false, 2), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain4.com", -1, s_cultureGb, false, 3), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain1.org", -1, s_cultureGb, false, 4), current), // yes: same site (though bogus setup)
|
||||
},
|
||||
current,
|
||||
true,
|
||||
s_cultureFr,
|
||||
s_cultureFr).ToArray();
|
||||
|
||||
Assert.AreEqual(1, output.Count());
|
||||
Assert.Contains("http://domain1.org/", output.Select(d => d.Uri.ToString()).ToArray());
|
||||
Assert.AreEqual(1, output.Length);
|
||||
Assert.AreEqual("http://domain1.org/", output[0].Uri.ToString());
|
||||
|
||||
siteDomainMapper.BindSites("site1", "site3");
|
||||
siteDomainMapper.BindSites("site2", "site4");
|
||||
@@ -346,43 +346,43 @@ public class SiteDomainMapperTests
|
||||
output = siteDomainMapper.MapDomains(
|
||||
new[]
|
||||
{
|
||||
new DomainAndUri(new Domain(1, "domain1.com", -1, s_cultureFr, false), current), // no: current + what MapDomain would pick
|
||||
new DomainAndUri(new Domain(1, "domain2.com", -1, s_cultureGb, false), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain3.com", -1, s_cultureGb, false), current), // yes: bound site
|
||||
new DomainAndUri(new Domain(1, "domain3.org", -1, s_cultureGb, false), current), // yes: bound site
|
||||
new DomainAndUri(new Domain(1, "domain4.com", -1, s_cultureGb, false), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain1.org", -1, s_cultureGb, false), current), // yes: same site (though bogus setup)
|
||||
new DomainAndUri(new Domain(1, "domain1.com", -1, s_cultureFr, false, 0), current), // no: current + what MapDomain would pick
|
||||
new DomainAndUri(new Domain(1, "domain2.com", -1, s_cultureGb, false, 1), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain3.com", -1, s_cultureGb, false, 2), current), // yes: bound site
|
||||
new DomainAndUri(new Domain(1, "domain3.org", -1, s_cultureGb, false, 3), current), // yes: bound site
|
||||
new DomainAndUri(new Domain(1, "domain4.com", -1, s_cultureGb, false, 4), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain1.org", -1, s_cultureGb, false, 5), current), // yes: same site (though bogus setup)
|
||||
},
|
||||
current,
|
||||
true,
|
||||
s_cultureFr,
|
||||
s_cultureFr).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());
|
||||
Assert.AreEqual(3, output.Length);
|
||||
Assert.AreEqual("http://domain3.com/", output[0].Uri.ToString());
|
||||
Assert.AreEqual("http://domain3.org/", output[1].Uri.ToString());
|
||||
Assert.AreEqual("http://domain1.org/", output[2].Uri.ToString());
|
||||
|
||||
// current is a site1 uri, domains does not contain current
|
||||
current = new Uri("http://domain1.com/foo/bar");
|
||||
output = siteDomainMapper.MapDomains(
|
||||
new[]
|
||||
{
|
||||
new DomainAndUri(new Domain(1, "domain1.net", -1, s_cultureFr, false), current), // no: what MapDomain would pick
|
||||
new DomainAndUri(new Domain(1, "domain2.com", -1, s_cultureGb, false), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain3.com", -1, s_cultureGb, false), current), // yes: bound site
|
||||
new DomainAndUri(new Domain(1, "domain3.org", -1, s_cultureGb, false), current), // yes: bound site
|
||||
new DomainAndUri(new Domain(1, "domain4.com", -1, s_cultureGb, false), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain1.org", -1, s_cultureGb, false), current), // yes: same site (though bogus setup)
|
||||
new DomainAndUri(new Domain(1, "domain1.net", -1, s_cultureFr, false, 0), current), // no: what MapDomain would pick
|
||||
new DomainAndUri(new Domain(1, "domain2.com", -1, s_cultureGb, false, 1), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain3.com", -1, s_cultureGb, false, 2), current), // yes: bound site
|
||||
new DomainAndUri(new Domain(1, "domain3.org", -1, s_cultureGb, false, 3), current), // yes: bound site
|
||||
new DomainAndUri(new Domain(1, "domain4.com", -1, s_cultureGb, false, 4), current), // no: not same site
|
||||
new DomainAndUri(new Domain(1, "domain1.org", -1, s_cultureGb, false, 5), current), // yes: same site (though bogus setup)
|
||||
},
|
||||
current,
|
||||
true,
|
||||
s_cultureFr,
|
||||
s_cultureFr).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());
|
||||
Assert.AreEqual(3, output.Length);
|
||||
Assert.AreEqual("http://domain3.com/", output[0].Uri.ToString());
|
||||
Assert.AreEqual("http://domain3.org/", output[1].Uri.ToString());
|
||||
Assert.AreEqual("http://domain1.org/", output[2].Uri.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ public class UrlsProviderWithDomainsTests : UrlRoutingTestBase
|
||||
{
|
||||
new UmbracoDomain("domain1.com")
|
||||
{
|
||||
Id = 1, LanguageId = LangFrId, RootContentId = 1001, LanguageIsoCode = "fr-FR",
|
||||
Id = 1, LanguageId = LangFrId, RootContentId = 1001, LanguageIsoCode = "fr-FR", SortOrder = 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -38,7 +38,7 @@ public class UrlsProviderWithDomainsTests : UrlRoutingTestBase
|
||||
{
|
||||
new UmbracoDomain("http://domain1.com/foo")
|
||||
{
|
||||
Id = 1, LanguageId = LangFrId, RootContentId = 1001, LanguageIsoCode = "fr-FR",
|
||||
Id = 1, LanguageId = LangFrId, RootContentId = 1001, LanguageIsoCode = "fr-FR", SortOrder = 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -52,7 +52,7 @@ public class UrlsProviderWithDomainsTests : UrlRoutingTestBase
|
||||
{
|
||||
new UmbracoDomain("http://domain1.com/")
|
||||
{
|
||||
Id = 1, LanguageId = LangFrId, RootContentId = 10011, LanguageIsoCode = "fr-FR",
|
||||
Id = 1, LanguageId = LangFrId, RootContentId = 10011, LanguageIsoCode = "fr-FR", SortOrder = 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -66,27 +66,27 @@ public class UrlsProviderWithDomainsTests : UrlRoutingTestBase
|
||||
{
|
||||
new UmbracoDomain("http://domain1.com/")
|
||||
{
|
||||
Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US",
|
||||
Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US", SortOrder = 0,
|
||||
},
|
||||
new UmbracoDomain("http://domain1.com/en")
|
||||
{
|
||||
Id = 2, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US",
|
||||
Id = 2, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US", SortOrder = 0,
|
||||
},
|
||||
new UmbracoDomain("http://domain1.com/fr")
|
||||
{
|
||||
Id = 3, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR",
|
||||
Id = 3, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR", SortOrder = 0,
|
||||
},
|
||||
new UmbracoDomain("http://domain3.com/")
|
||||
{
|
||||
Id = 4, LanguageId = LangEngId, RootContentId = 1003, LanguageIsoCode = "en-US",
|
||||
Id = 4, LanguageId = LangEngId, RootContentId = 1003, LanguageIsoCode = "en-US", SortOrder = 0,
|
||||
},
|
||||
new UmbracoDomain("http://domain3.com/en")
|
||||
{
|
||||
Id = 5, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US",
|
||||
Id = 5, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US", SortOrder = 0,
|
||||
},
|
||||
new UmbracoDomain("http://domain3.com/fr")
|
||||
{
|
||||
Id = 6, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR",
|
||||
Id = 6, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR", SortOrder = 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -100,35 +100,35 @@ public class UrlsProviderWithDomainsTests : UrlRoutingTestBase
|
||||
{
|
||||
new UmbracoDomain("http://domain1.com/en")
|
||||
{
|
||||
Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US",
|
||||
Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US", SortOrder = 0,
|
||||
},
|
||||
new UmbracoDomain("http://domain1a.com/en")
|
||||
{
|
||||
Id = 2, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US",
|
||||
Id = 2, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US", SortOrder = 1,
|
||||
},
|
||||
new UmbracoDomain("http://domain1b.com/en")
|
||||
{
|
||||
Id = 3, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US",
|
||||
Id = 3, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US", SortOrder = 2,
|
||||
},
|
||||
new UmbracoDomain("http://domain1.com/fr")
|
||||
{
|
||||
Id = 4, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR",
|
||||
Id = 4, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR", SortOrder = 0,
|
||||
},
|
||||
new UmbracoDomain("http://domain1a.com/fr")
|
||||
{
|
||||
Id = 5, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR",
|
||||
Id = 5, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR", SortOrder = 1,
|
||||
},
|
||||
new UmbracoDomain("http://domain1b.com/fr")
|
||||
{
|
||||
Id = 6, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR",
|
||||
Id = 6, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR", SortOrder = 2,
|
||||
},
|
||||
new UmbracoDomain("http://domain3.com/en")
|
||||
{
|
||||
Id = 7, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US",
|
||||
Id = 7, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US", SortOrder = 0,
|
||||
},
|
||||
new UmbracoDomain("http://domain3.com/fr")
|
||||
{
|
||||
Id = 8, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR",
|
||||
Id = 8, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR", SortOrder = 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -478,7 +478,7 @@ public class UrlsProviderWithDomainsTests : UrlRoutingTestBase
|
||||
}
|
||||
|
||||
Assert.AreEqual(2, result.Length);
|
||||
Assert.AreEqual(result[0].Text, "http://domain1b.com/en/1001-1-1/");
|
||||
Assert.AreEqual(result[1].Text, "http://domain1a.com/en/1001-1-1/");
|
||||
Assert.AreEqual(result[0].Text, "http://domain1a.com/en/1001-1-1/");
|
||||
Assert.AreEqual(result[1].Text, "http://domain1b.com/en/1001-1-1/");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public class PublishedRequestBuilderTests
|
||||
|
||||
sut.SetDomain(
|
||||
new DomainAndUri(
|
||||
new Domain(1, "test", 2, "en-AU", false), new Uri("https://example.com/en-au")));
|
||||
new Domain(1, "test", 2, "en-AU", false, 0), new Uri("https://example.com/en-au")));
|
||||
|
||||
Assert.IsNotNull(sut.Domain);
|
||||
Assert.IsNotNull(sut.Culture);
|
||||
@@ -64,7 +64,7 @@ public class PublishedRequestBuilderTests
|
||||
var auCulture = "en-AU";
|
||||
var usCulture = "en-US";
|
||||
var domain = new DomainAndUri(
|
||||
new Domain(1, "test", 2, auCulture, false), new Uri("https://example.com/en-au"));
|
||||
new Domain(1, "test", 2, auCulture, false, 0), new Uri("https://example.com/en-au"));
|
||||
IReadOnlyDictionary<string, string> headers = new Dictionary<string, string> { ["Hello"] = "world" };
|
||||
var redirect = "https://test.com";
|
||||
|
||||
|
||||
@@ -242,6 +242,15 @@ public class PublishedContentLanguageVariantTests : PublishedSnapshotServiceTest
|
||||
Assert.IsNull(value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Get_Content_For_Unpopulated_Requested_DefaultLanguage_With_Fallback()
|
||||
{
|
||||
var snapshot = GetPublishedSnapshot();
|
||||
var content = snapshot.Content.GetAtRoot().First();
|
||||
var value = content.Value(PublishedValueFallback, "welcomeText", "fr", fallback: Fallback.ToDefaultLanguage);
|
||||
Assert.AreEqual("Welcome", value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Do_Not_Get_Content_Recursively_Unless_Requested()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user