Merge branch 'v8/8.17' into v9/feature/merge_v8.17-rc
This commit is contained in:
@@ -82,27 +82,35 @@ namespace Umbraco.Cms.Core.Models.ContentEditing
|
||||
yield return validationResult;
|
||||
}
|
||||
|
||||
var duplicateGroups = Groups.GroupBy(x => x.Name).Where(x => x.Count() > 1).ToArray();
|
||||
if (duplicateGroups.Any())
|
||||
foreach (var duplicateGroupAlias in Groups.GroupBy(x => x.Alias).Where(x => x.Count() > 1))
|
||||
{
|
||||
//we need to return the field name with an index so it's wired up correctly
|
||||
var lastIndex = Groups.IndexOf(duplicateGroups.Last().Last());
|
||||
yield return new ValidationResult("Duplicate group names not allowed", new[]
|
||||
var lastGroupIndex = Groups.IndexOf(duplicateGroupAlias.Last());
|
||||
yield return new ValidationResult("Duplicate aliases are not allowed: " + duplicateGroupAlias.Key, new[]
|
||||
{
|
||||
$"Groups[{lastIndex}].Name"
|
||||
// TODO: We don't display the alias yet, so add the validation message to the name
|
||||
$"Groups[{lastGroupIndex}].Name"
|
||||
});
|
||||
}
|
||||
|
||||
var duplicateProperties = Groups.SelectMany(x => x.Properties).Where(x => x.Inherited == false).GroupBy(x => x.Alias).Where(x => x.Count() > 1).ToArray();
|
||||
if (duplicateProperties.Any())
|
||||
foreach (var duplicateGroupName in Groups.GroupBy(x => (x.GetParentAlias(), x.Name)).Where(x => x.Count() > 1))
|
||||
{
|
||||
//we need to return the field name with an index so it's wired up correctly
|
||||
var lastProperty = duplicateProperties.Last().Last();
|
||||
var propertyGroup = Groups.Single(x => x.Properties.Contains(lastProperty));
|
||||
|
||||
yield return new ValidationResult("Duplicate property aliases not allowed: " + lastProperty.Alias, new[]
|
||||
var lastGroupIndex = Groups.IndexOf(duplicateGroupName.Last());
|
||||
yield return new ValidationResult("Duplicate names are not allowed", new[]
|
||||
{
|
||||
$"Groups[{propertyGroup.SortOrder}].Properties[{lastProperty.SortOrder}].Alias"
|
||||
$"Groups[{lastGroupIndex}].Name"
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var duplicatePropertyAlias in Groups.SelectMany(x => x.Properties).GroupBy(x => x.Alias).Where(x => x.Count() > 1))
|
||||
{
|
||||
var lastProperty = duplicatePropertyAlias.Last();
|
||||
var propertyGroup = Groups.Single(x => x.Properties.Contains(lastProperty));
|
||||
var lastPropertyIndex = propertyGroup.Properties.IndexOf(lastProperty);
|
||||
var propertyGroupIndex = Groups.IndexOf(propertyGroup);
|
||||
|
||||
yield return new ValidationResult("Duplicate property aliases not allowed: " + duplicatePropertyAlias.Key, new[]
|
||||
{
|
||||
$"Groups[{propertyGroupIndex}].Properties[{lastPropertyIndex}].Alias"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models.ContentEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// A model for retrieving multiple content types based on their aliases.
|
||||
/// </summary>
|
||||
[DataContract(Name = "contentTypes", Namespace = "")]
|
||||
public class ContentTypesByAliases
|
||||
{
|
||||
/// <summary>
|
||||
/// Id of the parent of the content type.
|
||||
/// </summary>
|
||||
[DataMember(Name = "parentId")]
|
||||
[Required]
|
||||
public int ParentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The alias of every content type to get.
|
||||
/// </summary>
|
||||
[DataMember(Name = "contentTypeAliases")]
|
||||
[Required]
|
||||
public string[] ContentTypeAliases { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
@@ -32,12 +33,22 @@ namespace Umbraco.Cms.Core.Models.ContentEditing
|
||||
[DataMember(Name = "id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[DataMember(Name = "sortOrder")]
|
||||
public int SortOrder { get; set; }
|
||||
[DataMember(Name = "key")]
|
||||
public Guid Key { get; set; }
|
||||
|
||||
[DataMember(Name = "type")]
|
||||
public PropertyGroupType Type { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataMember(Name = "name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataMember(Name = "alias")]
|
||||
public string Alias { get; set; }
|
||||
|
||||
[DataMember(Name = "sortOrder")]
|
||||
public int SortOrder { get; set; }
|
||||
}
|
||||
|
||||
[DataContract(Name = "propertyGroup", Namespace = "")]
|
||||
@@ -52,4 +63,10 @@ namespace Umbraco.Cms.Core.Models.ContentEditing
|
||||
[DataMember(Name = "properties")]
|
||||
public IEnumerable<TPropertyType> Properties { get; set; }
|
||||
}
|
||||
|
||||
internal static class PropertyGroupBasicExtensions
|
||||
{
|
||||
public static string GetParentAlias(this PropertyGroupBasic propertyGroup)
|
||||
=> PropertyGroupExtensions.GetParentAlias(propertyGroup.Alias);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models.ContentEditing
|
||||
@@ -12,6 +13,12 @@ namespace Umbraco.Cms.Core.Models.ContentEditing
|
||||
[DataMember(Name = "id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[DataMember(Name = "key")]
|
||||
public Guid Key { get; set; }
|
||||
|
||||
[DataMember(Name = "type")]
|
||||
public int Type { get; set; }
|
||||
|
||||
[DataMember(Name = "active")]
|
||||
public bool IsActive { get; set; }
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
@@ -302,21 +302,19 @@ namespace Umbraco.Cms.Core.Models
|
||||
/// <returns>Returns <c>True</c> if a PropertyType with the passed in alias exists, otherwise <c>False</c></returns>
|
||||
public abstract bool PropertyTypeExists(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PropertyGroup.
|
||||
/// This method will also check if a group already exists with the same name and link it to the parent.
|
||||
/// </summary>
|
||||
/// <param name="groupName">Name of the PropertyGroup to add</param>
|
||||
/// <returns>Returns <c>True</c> if a PropertyGroup with the passed in name was added, otherwise <c>False</c></returns>
|
||||
public abstract bool AddPropertyGroup(string groupName);
|
||||
/// <inheritdoc />
|
||||
[Obsolete("Use AddPropertyGroup(name, alias) instead to explicitly set the alias.")]
|
||||
public virtual bool AddPropertyGroup(string groupName) => AddPropertyGroup(groupName, groupName.ToSafeAlias(_shortStringHelper, true));
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PropertyType to a specific PropertyGroup
|
||||
/// </summary>
|
||||
/// <param name="propertyType"><see cref="IPropertyType"/> to add</param>
|
||||
/// <param name="propertyGroupName">Name of the PropertyGroup to add the PropertyType to</param>
|
||||
/// <returns>Returns <c>True</c> if PropertyType was added, otherwise <c>False</c></returns>
|
||||
public abstract bool AddPropertyType(IPropertyType propertyType, string propertyGroupName);
|
||||
/// <inheritdoc />
|
||||
public abstract bool AddPropertyGroup(string name, string alias);
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete("Use AddPropertyType(propertyType, groupAlias, groupName) instead to explicitly set the alias of the group (note the slighty different parameter order).")]
|
||||
public virtual bool AddPropertyType(IPropertyType propertyType, string propertyGroupName) => AddPropertyType(propertyType, propertyGroupName.ToSafeAlias(_shortStringHelper, true), propertyGroupName);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract bool AddPropertyType(IPropertyType propertyType, string groupAlias, string groupName);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PropertyType, which does not belong to a PropertyGroup.
|
||||
@@ -344,18 +342,20 @@ namespace Umbraco.Cms.Core.Models
|
||||
/// "generic properties" ie does not have a tab anymore.</remarks>
|
||||
public bool MovePropertyType(string propertyTypeAlias, string propertyGroupName)
|
||||
{
|
||||
// note: not dealing with alias casing at all here?
|
||||
|
||||
// get property, ensure it exists
|
||||
var propertyType = PropertyTypes.FirstOrDefault(x => x.Alias == propertyTypeAlias);
|
||||
if (propertyType == null) return false;
|
||||
|
||||
// get new group, if required, and ensure it exists
|
||||
var newPropertyGroup = propertyGroupName == null
|
||||
? null
|
||||
: PropertyGroups.FirstOrDefault(x => x.Name == propertyGroupName);
|
||||
if (propertyGroupName != null && newPropertyGroup == null) return false;
|
||||
PropertyGroup newPropertyGroup = null;
|
||||
if (propertyGroupName != null)
|
||||
{
|
||||
var index = PropertyGroups.IndexOfKey(propertyGroupName);
|
||||
if (index == -1) return false;
|
||||
|
||||
newPropertyGroup = PropertyGroups[index];
|
||||
}
|
||||
|
||||
// get old group
|
||||
var oldPropertyGroup = PropertyGroups.FirstOrDefault(x =>
|
||||
x.PropertyTypes.Any(y => y.Alias == propertyTypeAlias));
|
||||
@@ -408,11 +408,13 @@ namespace Umbraco.Cms.Core.Models
|
||||
public void RemovePropertyGroup(string propertyGroupName)
|
||||
{
|
||||
// if no group exists with that name, do nothing
|
||||
var group = PropertyGroups[propertyGroupName];
|
||||
if (group == null) return;
|
||||
var index = PropertyGroups.IndexOfKey(propertyGroupName);
|
||||
if (index == -1) return;
|
||||
|
||||
var group = PropertyGroups[index];
|
||||
|
||||
// first remove the group
|
||||
PropertyGroups.RemoveItem(propertyGroupName);
|
||||
PropertyGroups.Remove(group);
|
||||
|
||||
// Then re-assign the group's properties to no group
|
||||
foreach (var property in group.PropertyTypes)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
@@ -65,15 +65,14 @@ namespace Umbraco.Cms.Core.Models
|
||||
propertyType.ResetDirtyProperties(false);
|
||||
}
|
||||
|
||||
return ContentTypeComposition.SelectMany(x => x.CompositionPropertyGroups)
|
||||
return PropertyGroups.Union(ContentTypeComposition.SelectMany(x => x.CompositionPropertyGroups)
|
||||
.Select(group =>
|
||||
{
|
||||
group = (PropertyGroup) group.DeepClone();
|
||||
foreach (var property in group.PropertyTypes)
|
||||
AcquireProperty(property);
|
||||
return group;
|
||||
})
|
||||
.Union(PropertyGroups);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,29 +201,29 @@ namespace Umbraco.Cms.Core.Models
|
||||
return CompositionPropertyTypes.Any(x => x.Alias == propertyTypeAlias);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PropertyGroup.
|
||||
/// </summary>
|
||||
/// <param name="groupName">Name of the PropertyGroup to add</param>
|
||||
/// <returns>Returns <c>True</c> if a PropertyGroup with the passed in name was added, otherwise <c>False</c></returns>
|
||||
public override bool AddPropertyGroup(string groupName)
|
||||
/// <inheritdoc />
|
||||
public override bool AddPropertyGroup(string name, string alias)
|
||||
{
|
||||
return AddAndReturnPropertyGroup(groupName) != null;
|
||||
return AddAndReturnPropertyGroup(name, alias) != null;
|
||||
}
|
||||
|
||||
private PropertyGroup AddAndReturnPropertyGroup(string name)
|
||||
private PropertyGroup AddAndReturnPropertyGroup(string name, string alias)
|
||||
{
|
||||
// ensure we don't have it already
|
||||
if (PropertyGroups.Any(x => x.Name == name))
|
||||
// Ensure we don't have it already
|
||||
if (PropertyGroups.Contains(alias))
|
||||
return null;
|
||||
|
||||
// create the new group
|
||||
var group = new PropertyGroup(SupportsPublishing) { Name = name, SortOrder = 0 };
|
||||
// Add new group
|
||||
var group = new PropertyGroup(SupportsPublishing)
|
||||
{
|
||||
Name = name,
|
||||
Alias = alias
|
||||
};
|
||||
|
||||
// check if it is inherited - there might be more than 1 but we want the 1st, to
|
||||
// reuse its sort order - if there are more than 1 and they have different sort
|
||||
// orders... there isn't much we can do anyways
|
||||
var inheritGroup = CompositionPropertyGroups.FirstOrDefault(x => x.Name == name);
|
||||
var inheritGroup = CompositionPropertyGroups.FirstOrDefault(x => x.Alias == alias);
|
||||
if (inheritGroup == null)
|
||||
{
|
||||
// no, just local, set sort order
|
||||
@@ -244,24 +243,30 @@ namespace Umbraco.Cms.Core.Models
|
||||
return group;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PropertyType to a specific PropertyGroup
|
||||
/// </summary>
|
||||
/// <param name="propertyType"><see cref="IPropertyType"/> to add</param>
|
||||
/// <param name="propertyGroupName">Name of the PropertyGroup to add the PropertyType to</param>
|
||||
/// <returns>Returns <c>True</c> if PropertyType was added, otherwise <c>False</c></returns>
|
||||
public override bool AddPropertyType(IPropertyType propertyType, string propertyGroupName)
|
||||
/// <inheritdoc />
|
||||
public override bool AddPropertyType(IPropertyType propertyType, string groupAlias, string groupName)
|
||||
{
|
||||
// ensure no duplicate alias - over all composition properties
|
||||
if (PropertyTypeExists(propertyType.Alias))
|
||||
return false;
|
||||
|
||||
// get and ensure a group local to this content type
|
||||
var group = PropertyGroups.Contains(propertyGroupName)
|
||||
? PropertyGroups[propertyGroupName]
|
||||
: AddAndReturnPropertyGroup(propertyGroupName);
|
||||
if (group == null)
|
||||
PropertyGroup group;
|
||||
var index = PropertyGroups.IndexOfKey(groupAlias);
|
||||
if (index != -1)
|
||||
{
|
||||
group = PropertyGroups[index];
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(groupName))
|
||||
{
|
||||
group = AddAndReturnPropertyGroup(groupName, groupAlias);
|
||||
if (group == null) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No group name specified, so we can't create a new one and add the property type
|
||||
return false;
|
||||
}
|
||||
|
||||
// add property to group
|
||||
propertyType.PropertyGroupId = new Lazy<int>(() => group.Id);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models
|
||||
@@ -116,10 +117,10 @@ namespace Umbraco.Cms.Core.Models
|
||||
void RemovePropertyType(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a PropertyGroup from the current ContentType
|
||||
/// Removes a property group from the current content type.
|
||||
/// </summary>
|
||||
/// <param name="propertyGroupName">Name of the <see cref="PropertyGroup"/> to remove</param>
|
||||
void RemovePropertyGroup(string propertyGroupName);
|
||||
/// <param name="propertyGroupName">Name of the <see cref="PropertyGroup" /> to remove</param>
|
||||
void RemovePropertyGroup(string propertyGroupName); // TODO Rename to propertyGroupAlias
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a PropertyType with a given alias already exists
|
||||
@@ -129,13 +130,27 @@ namespace Umbraco.Cms.Core.Models
|
||||
bool PropertyTypeExists(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PropertyType to a specific PropertyGroup
|
||||
/// Adds the property type to the specified property group (creates a new group if not found).
|
||||
/// </summary>
|
||||
/// <param name="propertyType"><see cref="IPropertyType"/> to add</param>
|
||||
/// <param name="propertyGroupName">Name of the PropertyGroup to add the PropertyType to</param>
|
||||
/// <returns>Returns <c>True</c> if PropertyType was added, otherwise <c>False</c></returns>
|
||||
/// <param name="propertyType">The property type to add.</param>
|
||||
/// <param name="propertyGroupName">The name of the property group to add the property type to.</param>
|
||||
/// <returns>
|
||||
/// Returns <c>true</c> if the property type was added; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
[Obsolete("Use AddPropertyType(propertyType, groupAlias, groupName) instead to explicitly set the alias of the group (note the slighty different parameter order).")]
|
||||
bool AddPropertyType(IPropertyType propertyType, string propertyGroupName);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the property type to the specified property group (creates a new group if not found and a name is specified).
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The property type to add.</param>
|
||||
/// <param name="groupAlias">The alias of the property group to add the property type to.</param>
|
||||
/// <param name="groupName">The name of the property group to create when not found.</param>
|
||||
/// <returns>
|
||||
/// Returns <c>true</c> if the property type was added; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
bool AddPropertyType(IPropertyType propertyType, string groupAlias, string groupName); // TODO Make groupName optional (add null as default value) after removing obsolete overload
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PropertyType, which does not belong to a PropertyGroup.
|
||||
/// </summary>
|
||||
@@ -144,20 +159,38 @@ namespace Umbraco.Cms.Core.Models
|
||||
bool AddPropertyType(IPropertyType propertyType);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PropertyGroup.
|
||||
/// This method will also check if a group already exists with the same name and link it to the parent.
|
||||
/// Adds a property group with the alias based on the specified <paramref name="groupName" />.
|
||||
/// </summary>
|
||||
/// <param name="groupName">Name of the PropertyGroup to add</param>
|
||||
/// <returns>Returns <c>True</c> if a PropertyGroup with the passed in name was added, otherwise <c>False</c></returns>
|
||||
/// <param name="groupName">Name of the group.</param>
|
||||
/// <returns>
|
||||
/// Returns <c>true</c> if a property group with specified <paramref name="groupName" /> was added; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// This method will also check if a group already exists with the same alias.
|
||||
/// </remarks>
|
||||
[Obsolete("Use AddPropertyGroup(name, alias) instead to explicitly set the alias.")]
|
||||
bool AddPropertyGroup(string groupName);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a property group with the specified <paramref name="name" /> and <paramref name="alias" />.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the group.</param>
|
||||
/// <param name="alias">The alias.</param>
|
||||
/// <returns>
|
||||
/// Returns <c>true</c> if a property group with specified <paramref name="alias" /> was added; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// This method will also check if a group already exists with the same alias.
|
||||
/// </remarks>
|
||||
bool AddPropertyGroup(string name, string alias);
|
||||
|
||||
/// <summary>
|
||||
/// Moves a PropertyType to a specified PropertyGroup
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">Alias of the PropertyType to move</param>
|
||||
/// <param name="propertyGroupName">Name of the PropertyGroup to move the PropertyType to</param>
|
||||
/// <returns></returns>
|
||||
bool MovePropertyType(string propertyTypeAlias, string propertyGroupName);
|
||||
bool MovePropertyType(string propertyTypeAlias, string propertyGroupName); // TODO Rename to propertyGroupAlias
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="ISimpleContentType"/> corresponding to this content type.
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A group of property types, which corresponds to the properties grouped under a Tab.
|
||||
/// Represents a group of property types.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
[DebuggerDisplay("Id: {Id}, Name: {Name}")]
|
||||
[DebuggerDisplay("Id: {Id}, Name: {Name}, Alias: {Alias}")]
|
||||
public class PropertyGroup : EntityBase, IEquatable<PropertyGroup>
|
||||
{
|
||||
[SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "This field is for internal use only (to allow changing item keys).")]
|
||||
internal PropertyGroupCollection Collection;
|
||||
private PropertyGroupType _type;
|
||||
private string _name;
|
||||
private string _alias;
|
||||
private int _sortOrder;
|
||||
private PropertyTypeCollection _propertyTypes;
|
||||
|
||||
@@ -34,8 +38,24 @@ namespace Umbraco.Cms.Core.Models
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Name of the Group, which corresponds to the Tab-name in the UI
|
||||
/// Gets or sets the type of the group.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The type.
|
||||
/// </value>
|
||||
[DataMember]
|
||||
public PropertyGroupType Type
|
||||
{
|
||||
get => _type;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _type, nameof(Type));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the group.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name.
|
||||
/// </value>
|
||||
[DataMember]
|
||||
public string Name
|
||||
{
|
||||
@@ -44,8 +64,30 @@ namespace Umbraco.Cms.Core.Models
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Sort Order of the Group
|
||||
/// Gets or sets the alias of the group.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The alias.
|
||||
/// </value>
|
||||
[DataMember]
|
||||
public string Alias
|
||||
{
|
||||
get => _alias;
|
||||
set
|
||||
{
|
||||
// If added to a collection, ensure the key is changed before setting it (this ensures the internal lookup dictionary is updated)
|
||||
Collection?.ChangeKey(this, value);
|
||||
|
||||
SetPropertyValueAndDetectChanges(value, ref _alias, nameof(Alias));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sort order of the group.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The sort order.
|
||||
/// </value>
|
||||
[DataMember]
|
||||
public int SortOrder
|
||||
{
|
||||
@@ -54,10 +96,13 @@ namespace Umbraco.Cms.Core.Models
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a collection of PropertyTypes for this PropertyGroup
|
||||
/// Gets or sets a collection of property types for the group.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The property types.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Marked DoNotClone because we will manually deal with cloning and the event handlers
|
||||
/// Marked with DoNotClone, because we will manually deal with cloning and the event handlers.
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
[DoNotClone]
|
||||
@@ -83,30 +128,103 @@ namespace Umbraco.Cms.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(PropertyGroup other)
|
||||
{
|
||||
if (base.Equals(other)) return true;
|
||||
return other != null && Name.InvariantEquals(other.Name);
|
||||
}
|
||||
public bool Equals(PropertyGroup other) => base.Equals(other) || (other != null && Type == other.Type && Alias == other.Alias);
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var baseHash = base.GetHashCode();
|
||||
var nameHash = Name.ToLowerInvariant().GetHashCode();
|
||||
return baseHash ^ nameHash;
|
||||
}
|
||||
public override int GetHashCode() => (base.GetHashCode(), Type, Alias).GetHashCode();
|
||||
|
||||
protected override void PerformDeepClone(object clone)
|
||||
{
|
||||
base.PerformDeepClone(clone);
|
||||
|
||||
var clonedEntity = (PropertyGroup)clone;
|
||||
clonedEntity.Collection = null;
|
||||
|
||||
if (clonedEntity._propertyTypes != null)
|
||||
{
|
||||
clonedEntity._propertyTypes.ClearCollectionChangedEvents(); //clear this event handler if any
|
||||
clonedEntity._propertyTypes.ClearCollectionChangedEvents(); //clear this event handler if any
|
||||
clonedEntity._propertyTypes = (PropertyTypeCollection) _propertyTypes.DeepClone(); //manually deep clone
|
||||
clonedEntity._propertyTypes.CollectionChanged += clonedEntity.PropertyTypesChanged; //re-assign correct event handler
|
||||
clonedEntity._propertyTypes.CollectionChanged += clonedEntity.PropertyTypesChanged; //re-assign correct event handler
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class PropertyGroupExtensions
|
||||
{
|
||||
private const char aliasSeparator = '/';
|
||||
|
||||
internal static string GetLocalAlias(string alias)
|
||||
{
|
||||
var lastIndex = alias?.LastIndexOf(aliasSeparator) ?? -1;
|
||||
if (lastIndex != -1)
|
||||
{
|
||||
return alias.Substring(lastIndex + 1);
|
||||
}
|
||||
|
||||
return alias;
|
||||
}
|
||||
|
||||
internal static string GetParentAlias(string alias)
|
||||
{
|
||||
var lastIndex = alias?.LastIndexOf(aliasSeparator) ?? -1;
|
||||
if (lastIndex == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return alias.Substring(0, lastIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the local alias.
|
||||
/// </summary>
|
||||
/// <param name="propertyGroup">The property group.</param>
|
||||
/// <returns>
|
||||
/// The local alias.
|
||||
/// </returns>
|
||||
public static string GetLocalAlias(this PropertyGroup propertyGroup) => GetLocalAlias(propertyGroup.Alias);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the local alias.
|
||||
/// </summary>
|
||||
/// <param name="propertyGroup">The property group.</param>
|
||||
/// <param name="localAlias">The local alias.</param>
|
||||
public static void UpdateLocalAlias(this PropertyGroup propertyGroup, string localAlias)
|
||||
{
|
||||
var parentAlias = propertyGroup.GetParentAlias();
|
||||
if (string.IsNullOrEmpty(parentAlias))
|
||||
{
|
||||
propertyGroup.Alias = localAlias;
|
||||
}
|
||||
else
|
||||
{
|
||||
propertyGroup.Alias = parentAlias + aliasSeparator + localAlias;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent alias.
|
||||
/// </summary>
|
||||
/// <param name="propertyGroup">The property group.</param>
|
||||
/// <returns>
|
||||
/// The parent alias.
|
||||
/// </returns>
|
||||
public static string GetParentAlias(this PropertyGroup propertyGroup) => GetParentAlias(propertyGroup.Alias);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the parent alias.
|
||||
/// </summary>
|
||||
/// <param name="propertyGroup">The property group.</param>
|
||||
/// <param name="parentAlias">The parent alias.</param>
|
||||
public static void UpdateParentAlias(this PropertyGroup propertyGroup, string parentAlias)
|
||||
{
|
||||
var localAlias = propertyGroup.GetLocalAlias();
|
||||
if (string.IsNullOrEmpty(parentAlias))
|
||||
{
|
||||
propertyGroup.Alias = localAlias;
|
||||
}
|
||||
else
|
||||
{
|
||||
propertyGroup.Alias = parentAlias + aliasSeparator + localAlias;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Represents a collection of <see cref="PropertyGroup"/> objects
|
||||
/// </summary>
|
||||
@@ -16,9 +16,16 @@ namespace Umbraco.Cms.Core.Models
|
||||
// TODO: Change this to ObservableDictionary so we can reduce the INotifyCollectionChanged implementation details
|
||||
public class PropertyGroupCollection : KeyedCollection<string, PropertyGroup>, INotifyCollectionChanged, IDeepCloneable
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PropertyGroupCollection" /> class.
|
||||
/// </summary>
|
||||
public PropertyGroupCollection()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PropertyGroupCollection" /> class.
|
||||
/// </summary>
|
||||
/// <param name="groups">The groups.</param>
|
||||
public PropertyGroupCollection(IEnumerable<PropertyGroup> groups)
|
||||
{
|
||||
Reset(groups);
|
||||
@@ -31,10 +38,10 @@ namespace Umbraco.Cms.Core.Models
|
||||
/// <remarks></remarks>
|
||||
internal void Reset(IEnumerable<PropertyGroup> groups)
|
||||
{
|
||||
//collection events will be raised in each of these calls
|
||||
// Collection events will be raised in each of these calls
|
||||
Clear();
|
||||
|
||||
//collection events will be raised in each of these calls
|
||||
// Collection events will be raised in each of these calls
|
||||
foreach (var group in groups)
|
||||
Add(group);
|
||||
}
|
||||
@@ -42,73 +49,87 @@ namespace Umbraco.Cms.Core.Models
|
||||
protected override void SetItem(int index, PropertyGroup item)
|
||||
{
|
||||
var oldItem = index >= 0 ? this[index] : item;
|
||||
|
||||
base.SetItem(index, item);
|
||||
|
||||
oldItem.Collection = null;
|
||||
item.Collection = this;
|
||||
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, oldItem));
|
||||
}
|
||||
|
||||
protected override void RemoveItem(int index)
|
||||
{
|
||||
var removed = this[index];
|
||||
|
||||
base.RemoveItem(index);
|
||||
|
||||
removed.Collection = null;
|
||||
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));
|
||||
}
|
||||
|
||||
protected override void InsertItem(int index, PropertyGroup item)
|
||||
{
|
||||
base.InsertItem(index, item);
|
||||
|
||||
item.Collection = this;
|
||||
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
|
||||
}
|
||||
|
||||
protected override void ClearItems()
|
||||
{
|
||||
foreach (var item in this)
|
||||
{
|
||||
item.Collection = null;
|
||||
}
|
||||
|
||||
base.ClearItems();
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
|
||||
public new void Add(PropertyGroup item)
|
||||
{
|
||||
//Note this is done to ensure existing groups can be renamed
|
||||
// Ensure alias is set
|
||||
if (string.IsNullOrEmpty(item.Alias))
|
||||
{
|
||||
throw new InvalidOperationException("Set an alias before adding the property group.");
|
||||
}
|
||||
|
||||
// Note this is done to ensure existing groups can be renamed
|
||||
if (item.HasIdentity && item.Id > 0)
|
||||
{
|
||||
var exists = Contains(item.Id);
|
||||
if (exists)
|
||||
var index = IndexOfKey(item.Id);
|
||||
if (index != -1)
|
||||
{
|
||||
var keyExists = Contains(item.Name);
|
||||
var keyExists = Contains(item.Alias);
|
||||
if (keyExists)
|
||||
throw new Exception($"Naming conflict: Changing the name of PropertyGroup '{item.Name}' would result in duplicates");
|
||||
throw new ArgumentException($"Naming conflict: changing the alias of property group '{item.Name}' would result in duplicates.");
|
||||
|
||||
//collection events will be raised in SetItem
|
||||
SetItem(IndexOfKey(item.Id), item);
|
||||
// Collection events will be raised in SetItem
|
||||
SetItem(index, item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var key = GetKeyForItem(item);
|
||||
if (key != null)
|
||||
var index = IndexOfKey(item.Alias);
|
||||
if (index != -1)
|
||||
{
|
||||
var exists = Contains(key);
|
||||
if (exists)
|
||||
{
|
||||
//collection events will be raised in SetItem
|
||||
SetItem(IndexOfKey(key), item);
|
||||
return;
|
||||
}
|
||||
// Collection events will be raised in SetItem
|
||||
SetItem(index, item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
//collection events will be raised in InsertItem
|
||||
|
||||
// Collection events will be raised in InsertItem
|
||||
base.Add(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this collection contains a <see cref="PropertyGroup"/> whose name matches the specified parameter.
|
||||
/// </summary>
|
||||
/// <param name="groupName">Name of the PropertyGroup.</param>
|
||||
/// <returns><c>true</c> if the collection contains the specified name; otherwise, <c>false</c>.</returns>
|
||||
/// <remarks></remarks>
|
||||
public new bool Contains(string groupName)
|
||||
internal void ChangeKey(PropertyGroup item, string newKey)
|
||||
{
|
||||
return this.Any(x => x.Name == groupName);
|
||||
ChangeItemKey(item, newKey);
|
||||
}
|
||||
|
||||
public bool Contains(int id)
|
||||
@@ -116,34 +137,12 @@ namespace Umbraco.Cms.Core.Models
|
||||
return this.Any(x => x.Id == id);
|
||||
}
|
||||
|
||||
public void RemoveItem(string propertyGroupName)
|
||||
{
|
||||
var key = IndexOfKey(propertyGroupName);
|
||||
//Only removes an item if the key was found
|
||||
if (key != -1)
|
||||
RemoveItem(key);
|
||||
}
|
||||
|
||||
public int IndexOfKey(string key)
|
||||
{
|
||||
for (var i = 0; i < Count; i++)
|
||||
if (this[i].Name == key)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
public int IndexOfKey(string key) => this.FindIndex(x => x.Alias == key);
|
||||
|
||||
public int IndexOfKey(int id)
|
||||
{
|
||||
for (var i = 0; i < Count; i++)
|
||||
if (this[i].Id == id)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
public int IndexOfKey(int id) => this.FindIndex(x => x.Id == id);
|
||||
|
||||
protected override string GetKeyForItem(PropertyGroup item)
|
||||
{
|
||||
return item.Name;
|
||||
}
|
||||
protected override string GetKeyForItem(PropertyGroup item) => item.Alias;
|
||||
|
||||
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||
|
||||
@@ -164,6 +163,7 @@ namespace Umbraco.Cms.Core.Models
|
||||
{
|
||||
clone.Add((PropertyGroup)group.DeepClone());
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
|
||||
17
src/Umbraco.Core/Models/PropertyGroupType.cs
Normal file
17
src/Umbraco.Core/Models/PropertyGroupType.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace Umbraco.Cms.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the type of a property group.
|
||||
/// </summary>
|
||||
public enum PropertyGroupType : short
|
||||
{
|
||||
/// <summary>
|
||||
/// Display property types in a group.
|
||||
/// </summary>
|
||||
Group = 0,
|
||||
/// <summary>
|
||||
/// Display property types in a tab.
|
||||
/// </summary>
|
||||
Tab = 1
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
@@ -10,6 +11,7 @@ namespace Umbraco.Cms.Core.Models.PublishedContent
|
||||
/// </summary>
|
||||
/// <remarks>Instances of the <see cref="PublishedContentType"/> class are immutable, ie
|
||||
/// if the content type changes, then a new class needs to be created.</remarks>
|
||||
[DebuggerDisplay("{Alias}")]
|
||||
public class PublishedContentType : IPublishedContentType
|
||||
{
|
||||
private readonly IPublishedPropertyType[] _propertyTypes;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models.PublishedContent
|
||||
{
|
||||
@@ -18,6 +19,7 @@ namespace Umbraco.Cms.Core.Models.PublishedContent
|
||||
/// Provides an abstract base class for <c>IPublishedContent</c> implementations that
|
||||
/// wrap and extend another <c>IPublishedContent</c>.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{Id}: {Name} ({ContentType?.Alias})")]
|
||||
public abstract class PublishedContentWrapped : IPublishedContent
|
||||
{
|
||||
private readonly IPublishedContent _content;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains culture specific values for <see cref="IPublishedContent"/>.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{Culture}")]
|
||||
public class PublishedCultureInfo
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models.PublishedContent
|
||||
{
|
||||
@@ -10,6 +11,7 @@ namespace Umbraco.Cms.Core.Models.PublishedContent
|
||||
/// if the data type changes, then a new class needs to be created.</para>
|
||||
/// <para>These instances should be created by an <see cref="IPublishedContentTypeFactory"/>.</para>
|
||||
/// </remarks>
|
||||
[DebuggerDisplay("{EditorAlias}")]
|
||||
public class PublishedDataType
|
||||
{
|
||||
private readonly Lazy<object> _lazyConfiguration;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models.PublishedContent
|
||||
@@ -7,6 +8,7 @@ namespace Umbraco.Cms.Core.Models.PublishedContent
|
||||
/// Provides a base class for <c>IPublishedProperty</c> implementations which converts and caches
|
||||
/// the value source to the actual value to use when rendering content.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{Alias} ({PropertyType?.EditorAlias})")]
|
||||
public abstract class PublishedPropertyBase : IPublishedProperty
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.XPath;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
@@ -10,6 +11,7 @@ namespace Umbraco.Cms.Core.Models.PublishedContent
|
||||
/// </summary>
|
||||
/// <remarks>Instances of the <see cref="PublishedPropertyType"/> class are immutable, ie
|
||||
/// if the property type changes, then a new class needs to be created.</remarks>
|
||||
[DebuggerDisplay("{Alias} ({EditorAlias})")]
|
||||
public class PublishedPropertyType : IPublishedPropertyType
|
||||
{
|
||||
private readonly IPublishedModelFactory _publishedModelFactory;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
namespace Umbraco.Cms.Core.Models.PublishedContent
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models.PublishedContent
|
||||
{
|
||||
[DebuggerDisplay("{Content?.Name} ({Score})")]
|
||||
public class PublishedSearchResult
|
||||
{
|
||||
public PublishedSearchResult(IPublishedContent content, float score)
|
||||
|
||||
Reference in New Issue
Block a user