Merge branch 'v8/8.17' into v9/feature/merge_v8.17-rc

This commit is contained in:
Ronald Barendse
2021-09-07 12:10:58 +02:00
335 changed files with 9119 additions and 2275 deletions

View File

@@ -1,4 +1,4 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Dictionary;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models.ContentEditing;
@@ -36,8 +36,11 @@ namespace Umbraco.Cms.Core.Models.Mapping
private void Map(PropertyGroup source, Tab<ContentPropertyDisplay> target, MapperContext mapper)
{
target.Id = source.Id;
target.IsActive = true;
target.Key = source.Key;
target.Type = (int)source.Type;
target.Label = source.Name;
target.Alias = source.Alias;
target.IsActive = true;
}
private void Map(IProperty source, ContentPropertyBasic target, MapperContext context)

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
@@ -319,7 +319,10 @@ namespace Umbraco.Cms.Core.Models.Mapping
{
if (source.Id > 0)
target.Id = source.Id;
target.Key = source.Key;
target.Type = source.Type;
target.Name = source.Name;
target.Alias = source.Alias;
target.SortOrder = source.SortOrder;
}
@@ -328,33 +331,38 @@ namespace Umbraco.Cms.Core.Models.Mapping
{
if (source.Id > 0)
target.Id = source.Id;
target.Key = source.Key;
target.Type = source.Type;
target.Name = source.Name;
target.Alias = source.Alias;
target.SortOrder = source.SortOrder;
}
// Umbraco.Code.MapAll -ContentTypeId -ParentTabContentTypes -ParentTabContentTypeNames
private static void Map(PropertyGroupBasic<PropertyTypeBasic> source, PropertyGroupDisplay<PropertyTypeDisplay> target, MapperContext context)
{
target.Inherited = source.Inherited;
if (source.Id > 0)
target.Id = source.Id;
target.Inherited = source.Inherited;
target.Key = source.Key;
target.Type = source.Type;
target.Name = source.Name;
target.Alias = source.Alias;
target.SortOrder = source.SortOrder;
target.Properties = context.MapEnumerable<PropertyTypeBasic, PropertyTypeDisplay>(source.Properties);
}
// Umbraco.Code.MapAll -ContentTypeId -ParentTabContentTypes -ParentTabContentTypeNames
private static void Map(PropertyGroupBasic<MemberPropertyTypeBasic> source, PropertyGroupDisplay<MemberPropertyTypeDisplay> target, MapperContext context)
{
target.Inherited = source.Inherited;
if (source.Id > 0)
target.Id = source.Id;
target.Inherited = source.Inherited;
target.Key = source.Key;
target.Type = source.Type;
target.Name = source.Name;
target.Alias = source.Alias;
target.SortOrder = source.SortOrder;
target.Properties = context.MapEnumerable<MemberPropertyTypeBasic, MemberPropertyTypeDisplay>(source.Properties);
}
@@ -452,6 +460,7 @@ namespace Umbraco.Cms.Core.Models.Mapping
var destOrigProperties = target.PropertyTypes.ToArray(); // all properties, in groups or not
var destGroups = new List<PropertyGroup>();
var sourceGroups = source.Groups.Where(x => x.IsGenericProperties == false).ToArray();
var sourceGroupParentAliases = sourceGroups.Select(x => x.GetParentAlias()).Distinct().ToArray();
foreach (var sourceGroup in sourceGroups)
{
// get the dest group
@@ -463,9 +472,9 @@ namespace Umbraco.Cms.Core.Models.Mapping
.Select(x => MapSaveProperty(x, destOrigProperties, context))
.ToArray();
// if the group has no local properties, skip it, ie sort-of garbage-collect
// if the group has no local properties and is not used as parent, skip it, ie sort-of garbage-collect
// local groups which would not have local properties anymore
if (destProperties.Length == 0)
if (destProperties.Length == 0 && !sourceGroupParentAliases.Contains(sourceGroup.Alias))
continue;
// ensure no duplicate alias, then assign the group properties collection
@@ -475,7 +484,7 @@ namespace Umbraco.Cms.Core.Models.Mapping
}
// ensure no duplicate name, then assign the groups collection
EnsureUniqueNames(destGroups);
EnsureUniqueAliases(destGroups);
target.PropertyGroups = new PropertyGroupCollection(destGroups);
// because the property groups collection was rebuilt, there is no need to remove
@@ -682,22 +691,22 @@ namespace Umbraco.Cms.Core.Models.Mapping
{
var propertiesA = properties.ToArray();
var distinctProperties = propertiesA
.Select(x => x.Alias.ToUpperInvariant())
.Select(x => x.Alias?.ToUpperInvariant())
.Distinct()
.Count();
if (distinctProperties != propertiesA.Length)
throw new InvalidOperationException("Cannot map properties due to alias conflict.");
}
private static void EnsureUniqueNames(IEnumerable<PropertyGroup> groups)
private static void EnsureUniqueAliases(IEnumerable<PropertyGroup> groups)
{
var groupsA = groups.ToArray();
var distinctProperties = groupsA
.Select(x => x.Name.ToUpperInvariant())
.Select(x => x.Alias)
.Distinct()
.Count();
if (distinctProperties != groupsA.Length)
throw new InvalidOperationException("Cannot map groups due to name conflict.");
throw new InvalidOperationException("Cannot map groups due to alias conflict.");
}
private static void MapComposition(ContentTypeSave source, IContentTypeComposition target, Func<string, IContentTypeComposition> getContentType)

View File

@@ -72,45 +72,50 @@ namespace Umbraco.Cms.Core.Models.Mapping
var groups = new List<PropertyGroupDisplay<TPropertyType>>();
// add groups local to this content type
foreach (var tab in source.PropertyGroups)
foreach (var propertyGroup in source.PropertyGroups)
{
var group = new PropertyGroupDisplay<TPropertyType>
{
Id = tab.Id,
Inherited = false,
Name = tab.Name,
SortOrder = tab.SortOrder,
Id = propertyGroup.Id,
Key = propertyGroup.Key,
Type = propertyGroup.Type,
Name = propertyGroup.Name,
Alias = propertyGroup.Alias,
SortOrder = propertyGroup.SortOrder,
Properties = MapProperties(propertyGroup.PropertyTypes, source, propertyGroup.Id, false),
ContentTypeId = source.Id
};
group.Properties = MapProperties(tab.PropertyTypes, source, tab.Id, false);
groups.Add(group);
}
// add groups inherited through composition
var localGroupIds = groups.Select(x => x.Id).ToArray();
foreach (var tab in source.CompositionPropertyGroups)
foreach (var propertyGroup in source.CompositionPropertyGroups)
{
// skip those that are local to this content type
if (localGroupIds.Contains(tab.Id)) continue;
if (localGroupIds.Contains(propertyGroup.Id)) continue;
// get the content type that defines this group
var definingContentType = GetContentTypeForPropertyGroup(source, tab.Id);
var definingContentType = GetContentTypeForPropertyGroup(source, propertyGroup.Id);
if (definingContentType == null)
throw new Exception("PropertyGroup with id=" + tab.Id + " was not found on any of the content type's compositions.");
throw new Exception("PropertyGroup with id=" + propertyGroup.Id + " was not found on any of the content type's compositions.");
var group = new PropertyGroupDisplay<TPropertyType>
{
Id = tab.Id,
Inherited = true,
Name = tab.Name,
SortOrder = tab.SortOrder,
Id = propertyGroup.Id,
Key = propertyGroup.Key,
Type = propertyGroup.Type,
Name = propertyGroup.Name,
Alias = propertyGroup.Alias,
SortOrder = propertyGroup.SortOrder,
Properties = MapProperties(propertyGroup.PropertyTypes, definingContentType, propertyGroup.Id, true),
ContentTypeId = definingContentType.Id,
ParentTabContentTypes = new[] { definingContentType.Id },
ParentTabContentTypeNames = new[] { definingContentType.Name }
};
group.Properties = MapProperties(tab.PropertyTypes, definingContentType, tab.Id, true);
groups.Add(group);
}
@@ -137,16 +142,16 @@ namespace Umbraco.Cms.Core.Models.Mapping
// if there are any generic properties, add the corresponding tab
if (genericProperties.Any())
{
var genericTab = new PropertyGroupDisplay<TPropertyType>
var genericGroup = new PropertyGroupDisplay<TPropertyType>
{
Id = PropertyGroupBasic.GenericPropertiesGroupId,
Name = "Generic properties",
ContentTypeId = source.Id,
SortOrder = 999,
Inherited = false,
Properties = genericProperties
Properties = genericProperties,
ContentTypeId = source.Id
};
groups.Add(genericTab);
groups.Add(genericGroup);
}
// handle locked properties
@@ -162,33 +167,33 @@ namespace Umbraco.Cms.Core.Models.Mapping
property.Locked = lockedPropertyAliases.Contains(property.Alias);
}
// now merge tabs based on names
// now merge tabs based on alias
// as for one name, we might have one local tab, plus some inherited tabs
var groupsGroupsByName = groups.GroupBy(x => x.Name).ToArray();
var groupsGroupsByAlias = groups.GroupBy(x => x.Alias).ToArray();
groups = new List<PropertyGroupDisplay<TPropertyType>>(); // start with a fresh list
foreach (var groupsByName in groupsGroupsByName)
foreach (var groupsByAlias in groupsGroupsByAlias)
{
// single group, just use it
if (groupsByName.Count() == 1)
if (groupsByAlias.Count() == 1)
{
groups.Add(groupsByName.First());
groups.Add(groupsByAlias.First());
continue;
}
// multiple groups, merge
var group = groupsByName.FirstOrDefault(x => x.Inherited == false) // try local
?? groupsByName.First(); // else pick one randomly
var group = groupsByAlias.FirstOrDefault(x => x.Inherited == false) // try local
?? groupsByAlias.First(); // else pick one randomly
groups.Add(group);
// in case we use the local one, flag as inherited
group.Inherited = true;
group.Inherited = true; // TODO Remove to allow changing sort order of the local one (and use the inherited group order below)
// merge (and sort) properties
var properties = groupsByName.SelectMany(x => x.Properties).OrderBy(x => x.SortOrder).ToArray();
var properties = groupsByAlias.SelectMany(x => x.Properties).OrderBy(x => x.SortOrder).ToArray();
group.Properties = properties;
// collect parent group info
var parentGroups = groupsByName.Where(x => x.ContentTypeId != source.Id).ToArray();
var parentGroups = groupsByAlias.Where(x => x.ContentTypeId != source.Id).ToArray();
group.ParentTabContentTypes = parentGroups.SelectMany(x => x.ParentTabContentTypes).ToArray();
group.ParentTabContentTypeNames = parentGroups.SelectMany(x => x.ParentTabContentTypeNames).ToArray();
}

View File

@@ -13,18 +13,17 @@ namespace Umbraco.Cms.Core.Models.Mapping
{
protected ICultureDictionary CultureDictionary { get; }
protected ILocalizedTextService LocalizedTextService { get; }
protected IEnumerable<string> IgnoreProperties { get; set; }
protected TabsAndPropertiesMapper(ICultureDictionary cultureDictionary, ILocalizedTextService localizedTextService)
: this(cultureDictionary, localizedTextService, new List<string>())
{ }
protected TabsAndPropertiesMapper(ICultureDictionary cultureDictionary, ILocalizedTextService localizedTextService, IEnumerable<string> ignoreProperties)
{
CultureDictionary = cultureDictionary ?? throw new ArgumentNullException(nameof(cultureDictionary));
LocalizedTextService = localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService));
IgnoreProperties = new List<string>();
}
protected TabsAndPropertiesMapper(ICultureDictionary cultureDictionary, ILocalizedTextService localizedTextService, IEnumerable<string> ignoreProperties)
: this(cultureDictionary, localizedTextService)
{
IgnoreProperties = ignoreProperties ?? throw new ArgumentNullException(nameof(ignoreProperties));
}
@@ -128,51 +127,48 @@ namespace Umbraco.Cms.Core.Models.Mapping
{
var tabs = new List<Tab<ContentPropertyDisplay>>();
// Property groups only exist on the content type (as it's only used for display purposes)
var contentType = _contentTypeBaseServiceProvider.GetContentTypeOf(source);
// add the tabs, for properties that belong to a tab
// need to aggregate the tabs, as content.PropertyGroups contains all the composition tabs,
// and there might be duplicates (content does not work like contentType and there is no
// content.CompositionPropertyGroups).
var groupsGroupsByName = contentType.CompositionPropertyGroups.OrderBy(x => x.SortOrder).GroupBy(x => x.Name);
foreach (var groupsByName in groupsGroupsByName)
// Merge the groups, as compositions can introduce duplicate aliases
var groups = contentType.CompositionPropertyGroups.OrderBy(x => x.SortOrder).ToArray();
var parentAliases = groups.Select(x => x.GetParentAlias()).Distinct().ToArray();
foreach (var groupsByAlias in groups.GroupBy(x => x.Alias))
{
var properties = new List<IProperty>();
// merge properties for groups with the same name
foreach (var group in groupsByName)
// Merge properties for groups with the same alias
foreach (var group in groupsByAlias)
{
var groupProperties = source.GetPropertiesForGroup(group)
.Where(x => IgnoreProperties.Contains(x.Alias) == false); // skip ignored
.Where(x => IgnoreProperties.Contains(x.Alias) == false); // Skip ignored properties
properties.AddRange(groupProperties);
}
if (properties.Count == 0)
if (properties.Count == 0 && !parentAliases.Contains(groupsByAlias.Key))
continue;
//map the properties
// Map the properties
var mappedProperties = MapProperties(source, properties, context);
// add the tab
// we need to pick an identifier... there is no "right" way...
var g = groupsByName.FirstOrDefault(x => x.Id == source.ContentTypeId) // try local
?? groupsByName.First(); // else pick one randomly
var groupId = g.Id;
var groupName = groupsByName.Key;
// Add the tab (the first is closest to the content type, e.g. local, then direct composition)
var g = groupsByAlias.First();
tabs.Add(new Tab<ContentPropertyDisplay>
{
Id = groupId,
Alias = groupName,
Label = LocalizedTextService.UmbracoDictionaryTranslate(CultureDictionary, groupName),
Properties = mappedProperties,
IsActive = false
Id = g.Id,
Key = g.Key,
Type = (int)g.Type,
Alias = g.Alias,
Label = LocalizedTextService.UmbracoDictionaryTranslate(CultureDictionary, g.Name),
Properties = mappedProperties
});
}
MapGenericProperties(source, tabs, context);
// activate the first tab, if any
// Activate the first tab, if any
if (tabs.Count > 0)
tabs[0].IsActive = true;