Merge branch 'v8/8.17' into v9/feature/merge_v8.17-rc
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user