Merge remote-tracking branch 'origin/dev-v7-contenttypeeditor' into 7.4.0
Conflicts: src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
@@ -30,6 +31,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes.
|
||||
/// </summary>
|
||||
[DataMember(Name = "notifications")]
|
||||
[ReadOnly(true)]
|
||||
public List<Notification> Notifications { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -43,6 +45,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// NOTE: The ProperCase is important because when we return ModeState normally it will always be proper case.
|
||||
/// </remarks>
|
||||
[DataMember(Name = "ModelState")]
|
||||
[ReadOnly(true)]
|
||||
public IDictionary<string, object> Errors { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models.Validation;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
@@ -15,10 +17,20 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
[DataContract(Name = "contentType", Namespace = "")]
|
||||
public class ContentTypeBasic : EntityBasic
|
||||
{
|
||||
/// <summary>
|
||||
/// Overridden to apply our own validation attributes since this is not always required for other classes
|
||||
/// </summary>
|
||||
[Required]
|
||||
[RegularExpression(@"^([a-zA-Z]\w.*)$", ErrorMessage = "Invalid alias")]
|
||||
[DataMember(Name = "alias")]
|
||||
public override string Alias { get; set; }
|
||||
|
||||
[DataMember(Name = "updateDate")]
|
||||
[ReadOnly(true)]
|
||||
public DateTime UpdateDate { get; set; }
|
||||
|
||||
[DataMember(Name = "createDate")]
|
||||
[ReadOnly(true)]
|
||||
public DateTime CreateDate { get; set; }
|
||||
|
||||
[DataMember(Name = "description")]
|
||||
@@ -31,6 +43,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// Returns true if the icon represents a CSS class instead of a file path
|
||||
/// </summary>
|
||||
[DataMember(Name = "iconIsClass")]
|
||||
[ReadOnly(true)]
|
||||
public bool IconIsClass
|
||||
{
|
||||
get
|
||||
@@ -48,6 +61,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// Returns the icon file path if the icon is not a class, otherwise returns an empty string
|
||||
/// </summary>
|
||||
[DataMember(Name = "iconFilePath")]
|
||||
[ReadOnly(true)]
|
||||
public string IconFilePath
|
||||
{
|
||||
get
|
||||
@@ -62,6 +76,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// Returns true if the icon represents a CSS class instead of a file path
|
||||
/// </summary>
|
||||
[DataMember(Name = "thumbnailIsClass")]
|
||||
[ReadOnly(true)]
|
||||
public bool ThumbnailIsClass
|
||||
{
|
||||
get
|
||||
@@ -79,6 +94,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// Returns the icon file path if the icon is not a class, otherwise returns an empty string
|
||||
/// </summary>
|
||||
[DataMember(Name = "thumbnailFilePath")]
|
||||
[ReadOnly(true)]
|
||||
public string ThumbnailFilePath
|
||||
{
|
||||
get
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Models.Validation;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
[DataContract(Name = "contentType", Namespace = "")]
|
||||
public class ContentTypeCompositionDisplay : ContentTypeBasic
|
||||
public class ContentTypeCompositionDisplay : ContentTypeBasic, INotificationModel
|
||||
{
|
||||
public ContentTypeCompositionDisplay()
|
||||
{
|
||||
@@ -17,15 +20,17 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
AllowedContentTypes = new List<int>();
|
||||
CompositeContentTypes = new List<string>();
|
||||
AvailableCompositeContentTypes = new List<EntityBasic>();
|
||||
Notifications = new List<Notification>();
|
||||
}
|
||||
|
||||
//name, alias, icon, thumb, desc, inherited from basic
|
||||
|
||||
//name, alias, icon, thumb, desc, inherited from basic
|
||||
|
||||
//List view
|
||||
[DataMember(Name = "isContainer")]
|
||||
public bool IsContainer { get; set; }
|
||||
|
||||
[DataMember(Name = "listViewEditorName")]
|
||||
[ReadOnly(true)]
|
||||
public string ListViewEditorName { get; set; }
|
||||
|
||||
//Tabs
|
||||
@@ -41,9 +46,31 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
public IEnumerable<string> CompositeContentTypes { get; set; }
|
||||
|
||||
[DataMember(Name = "availableCompositeContentTypes")]
|
||||
[ReadOnly(true)]
|
||||
public IEnumerable<EntityBasic> AvailableCompositeContentTypes { get; set; }
|
||||
|
||||
[DataMember(Name = "allowAsRoot")]
|
||||
public bool AllowAsRoot { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes.
|
||||
/// </summary>
|
||||
[DataMember(Name = "notifications")]
|
||||
[ReadOnly(true)]
|
||||
public List<Notification> Notifications { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is used for validation of a content item.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A content item can be invalid but still be saved. This occurs when there's property validation errors, we will
|
||||
/// still save the item but it cannot be published. So we need a way of returning validation errors as well as the
|
||||
/// updated model.
|
||||
///
|
||||
/// NOTE: The ProperCase is important because when we return ModeState normally it will always be proper case.
|
||||
/// </remarks>
|
||||
[DataMember(Name = "ModelState")]
|
||||
[ReadOnly(true)]
|
||||
public IDictionary<string, object> Errors { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
@@ -7,6 +8,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
|
||||
[DataContract(Name = "contentType", Namespace = "")]
|
||||
public class ContentTypeDisplay : ContentTypeCompositionDisplay
|
||||
{
|
||||
|
||||
91
src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs
Normal file
91
src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
[DataContract(Name = "contentType", Namespace = "")]
|
||||
public class ContentTypeSave : ContentTypeBasic, IValidatableObject
|
||||
{
|
||||
public ContentTypeSave()
|
||||
{
|
||||
//initialize collections so at least their never null
|
||||
Groups = new List<PropertyGroupBasic<PropertyTypeBasic>>();
|
||||
AllowedContentTypes = new List<int>();
|
||||
CompositeContentTypes = new List<string>();
|
||||
}
|
||||
|
||||
//Compositions
|
||||
[DataMember(Name = "compositeContentTypes")]
|
||||
public IEnumerable<string> CompositeContentTypes { get; set; }
|
||||
|
||||
[DataMember(Name = "isContainer")]
|
||||
public bool IsContainer { get; set; }
|
||||
|
||||
[DataMember(Name = "allowAsRoot")]
|
||||
public bool AllowAsRoot { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of allowed templates to assign (template alias)
|
||||
/// </summary>
|
||||
[DataMember(Name = "allowedTemplates")]
|
||||
public IEnumerable<string> AllowedTemplates { get; set; }
|
||||
|
||||
//Allowed child types
|
||||
[DataMember(Name = "allowedContentTypes")]
|
||||
public IEnumerable<int> AllowedContentTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The default template to assign (template alias)
|
||||
/// </summary>
|
||||
[DataMember(Name = "defaultTemplate")]
|
||||
public string DefaultTemplate { get; set; }
|
||||
|
||||
//Tabs
|
||||
[DataMember(Name = "groups")]
|
||||
public IEnumerable<PropertyGroupBasic<PropertyTypeBasic>> Groups { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Custom validation
|
||||
/// </summary>
|
||||
/// <param name="validationContext"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if (AllowedTemplates.Any(x => x.IsNullOrWhiteSpace()))
|
||||
yield return new ValidationResult("Template value cannot be null", new[] {"AllowedTemplates"});
|
||||
|
||||
if (CompositeContentTypes.Any(x => x.IsNullOrWhiteSpace()))
|
||||
yield return new ValidationResult("Composite Content Type value cannot be null", new[] { "CompositeContentTypes" });
|
||||
|
||||
var duplicateGroups = Groups.GroupBy(x => x.Name).Where(x => x.Count() > 1).ToArray();
|
||||
if (duplicateGroups.Any())
|
||||
{
|
||||
//we need to return the field name with an index so it's wired up correctly
|
||||
var firstIndex = Groups.IndexOf(duplicateGroups.First().First());
|
||||
yield return new ValidationResult("Duplicate group names not allowed", new[]
|
||||
{
|
||||
string.Format("Groups[{0}].Name", firstIndex)
|
||||
});
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
//we need to return the field name with an index so it's wired up correctly
|
||||
var firstProperty = duplicateProperties.First().First();
|
||||
var propertyGroup = Groups.Single(x => x.Properties.Contains(firstProperty));
|
||||
var groupIndex = Groups.IndexOf(propertyGroup);
|
||||
var propertyIndex = propertyGroup.Properties.IndexOf(firstProperty);
|
||||
|
||||
yield return new ValidationResult("Duplicate property aliases not allowed", new[]
|
||||
{
|
||||
string.Format("Groups[{0}].Properties[{1}].Alias", groupIndex, propertyIndex)
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
@@ -29,6 +30,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
public string Icon { get; set; }
|
||||
|
||||
[DataMember(Name = "trashed")]
|
||||
[ReadOnly(true)]
|
||||
public bool Trashed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -44,8 +46,11 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// <summary>
|
||||
/// This will only be populated for some entities like macros
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is overrideable to specify different validation attributes if required
|
||||
/// </remarks>
|
||||
[DataMember(Name = "alias")]
|
||||
public string Alias { get; set; }
|
||||
public virtual string Alias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path of the entity
|
||||
@@ -57,6 +62,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// A collection of extra data that is available for this specific entity/entity type
|
||||
/// </summary>
|
||||
[DataMember(Name = "metaData")]
|
||||
[ReadOnly(true)]
|
||||
public IDictionary<string, object> AdditionalData { get; private set; }
|
||||
}
|
||||
}
|
||||
|
||||
34
src/Umbraco.Web/Models/ContentEditing/PropertyGroupBasic.cs
Normal file
34
src/Umbraco.Web/Models/ContentEditing/PropertyGroupBasic.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
[DataContract(Name = "propertyGroup", Namespace = "")]
|
||||
public class PropertyGroupBasic<TPropertyType>
|
||||
where TPropertyType: PropertyTypeBasic
|
||||
{
|
||||
public PropertyGroupBasic()
|
||||
{
|
||||
Properties = new List<TPropertyType>();
|
||||
}
|
||||
|
||||
//Indicate if this tab was inherited
|
||||
[DataMember(Name = "inherited")]
|
||||
public bool Inherited { get; set; }
|
||||
|
||||
//TODO: Required ?
|
||||
[DataMember(Name = "id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[DataMember(Name = "properties")]
|
||||
public IEnumerable<TPropertyType> Properties { get; set; }
|
||||
|
||||
[DataMember(Name = "sortOrder")]
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataMember(Name = "name")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
@@ -8,7 +9,7 @@ using System.Threading.Tasks;
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
[DataContract(Name = "propertyGroup", Namespace = "")]
|
||||
public class PropertyGroupDisplay
|
||||
public class PropertyGroupDisplay : PropertyGroupBasic<PropertyTypeDisplay>
|
||||
{
|
||||
public PropertyGroupDisplay()
|
||||
{
|
||||
@@ -16,33 +17,22 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
ParentTabContentTypeNames = new List<string>();
|
||||
ParentTabContentTypes = new List<int>();
|
||||
}
|
||||
|
||||
[DataMember(Name = "id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
|
||||
[DataMember(Name = "parentGroupId")]
|
||||
[ReadOnly(true)]
|
||||
public int ParentGroupId { get; set; }
|
||||
|
||||
[DataMember(Name = "sortOrder")]
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
[DataMember(Name = "name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[DataMember(Name = "properties")]
|
||||
public IEnumerable<PropertyTypeDisplay> Properties { get; set; }
|
||||
|
||||
//Indicate if this tab was inherited
|
||||
[DataMember(Name = "inherited")]
|
||||
public bool Inherited { get; set; }
|
||||
|
||||
|
||||
//SD: Seems strange that this is required
|
||||
[DataMember(Name = "contentTypeId")]
|
||||
[ReadOnly(true)]
|
||||
public int ContentTypeId { get; set; }
|
||||
|
||||
[DataMember(Name = "parentTabContentTypes")]
|
||||
[ReadOnly(true)]
|
||||
public IEnumerable<int> ParentTabContentTypes { get; set; }
|
||||
|
||||
[DataMember(Name = "parentTabContentTypeNames")]
|
||||
[ReadOnly(true)]
|
||||
public IEnumerable<string> ParentTabContentTypeNames { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
43
src/Umbraco.Web/Models/ContentEditing/PropertyTypeBasic.cs
Normal file
43
src/Umbraco.Web/Models/ContentEditing/PropertyTypeBasic.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
[DataContract(Name = "propertyType")]
|
||||
public class PropertyTypeBasic
|
||||
{
|
||||
//indicates if this property was inherited
|
||||
[DataMember(Name = "inherited")]
|
||||
public bool Inherited { get; set; }
|
||||
|
||||
//TODO: Required ?
|
||||
[DataMember(Name = "id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[RegularExpression(@"^([a-zA-Z]\w.*)$", ErrorMessage = "Invalid alias")]
|
||||
[DataMember(Name = "alias")]
|
||||
public string Alias { get; set; }
|
||||
|
||||
[DataMember(Name = "description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[DataMember(Name = "validation")]
|
||||
public PropertyTypeValidation Validation { get; set; }
|
||||
|
||||
[DataMember(Name = "label")]
|
||||
[Required]
|
||||
public string Label { get; set; }
|
||||
|
||||
[DataMember(Name = "sortOrder")]
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
[DataMember(Name = "dataTypeId")]
|
||||
[Required]
|
||||
public int DataTypeId { get; set; }
|
||||
|
||||
//SD: Is this really needed ?
|
||||
[DataMember(Name = "groupId")]
|
||||
public int GroupId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
@@ -8,52 +9,28 @@ using System.Threading.Tasks;
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
[DataContract(Name = "propertyType")]
|
||||
public class PropertyTypeDisplay
|
||||
{
|
||||
[DataMember(Name = "id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[DataMember(Name = "alias")]
|
||||
public string Alias { get; set; }
|
||||
|
||||
[DataMember(Name = "description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
public class PropertyTypeDisplay : PropertyTypeBasic
|
||||
{
|
||||
[DataMember(Name = "editor")]
|
||||
[ReadOnly(true)]
|
||||
public string Editor { get; set; }
|
||||
|
||||
[DataMember(Name = "validation")]
|
||||
public PropertyTypeValidation Validation { get; set; }
|
||||
|
||||
[DataMember(Name = "label")]
|
||||
public string Label { get; set; }
|
||||
|
||||
|
||||
[DataMember(Name = "view")]
|
||||
[ReadOnly(true)]
|
||||
public string View { get; set; }
|
||||
|
||||
[DataMember(Name = "config")]
|
||||
[ReadOnly(true)]
|
||||
public IDictionary<string, object> Config { get; set; }
|
||||
|
||||
[DataMember(Name = "value")]
|
||||
public string Value { get; set; }
|
||||
|
||||
[DataMember(Name = "sortOrder")]
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
//indicates if this property was inherited
|
||||
[DataMember(Name = "inherited")]
|
||||
public bool Inherited { get; set; }
|
||||
|
||||
[DataMember(Name = "dataTypeId")]
|
||||
public int DataTypeId { get; set; }
|
||||
|
||||
[DataMember(Name = "groupId")]
|
||||
public int GroupId { get; set; }
|
||||
|
||||
|
||||
//SD: Seems strange that this is needed
|
||||
[DataMember(Name = "contentTypeId")]
|
||||
[ReadOnly(true)]
|
||||
public int ContentTypeId { get; set; }
|
||||
|
||||
//SD: Seems strange that this is needed
|
||||
[DataMember(Name = "contentTypeName")]
|
||||
[ReadOnly(true)]
|
||||
public string ContentTypeName { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,11 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class AvailableCompositeContentTypesResolver : ValueResolver<IContentTypeComposition, IEnumerable<EntityBasic>>
|
||||
{
|
||||
private ApplicationContext _context;
|
||||
private bool _mediaType;
|
||||
internal AvailableCompositeContentTypesResolver(ApplicationContext context, bool mediaType = false)
|
||||
private readonly ApplicationContext _context;
|
||||
|
||||
internal AvailableCompositeContentTypesResolver(ApplicationContext context)
|
||||
{
|
||||
_context = context;
|
||||
_mediaType = mediaType;
|
||||
}
|
||||
|
||||
protected override IEnumerable<EntityBasic> ResolveCore(IContentTypeComposition source)
|
||||
@@ -27,19 +26,19 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
var s = source;
|
||||
var type = _context.Services.EntityService.GetObjectType(source.Id);
|
||||
IContentTypeComposition[] allContentTypes = new IContentTypeComposition[0];
|
||||
var allContentTypes = new IContentTypeComposition[0];
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case UmbracoObjectTypes.DocumentType:
|
||||
case UmbracoObjectTypes.DocumentType:
|
||||
allContentTypes = _context.Services.ContentTypeService.GetAllContentTypes().Cast<IContentTypeComposition>().ToArray();
|
||||
break;
|
||||
|
||||
case UmbracoObjectTypes.MediaType:
|
||||
case UmbracoObjectTypes.MediaType:
|
||||
allContentTypes = _context.Services.ContentTypeService.GetAllMediaTypes().Cast<IContentTypeComposition>().ToArray();
|
||||
break;
|
||||
|
||||
case UmbracoObjectTypes.MemberType:
|
||||
case UmbracoObjectTypes.MemberType:
|
||||
allContentTypes = _context.Services.MemberTypeService.GetAll().Cast<IContentTypeComposition>().ToArray();
|
||||
break;
|
||||
}
|
||||
@@ -56,42 +55,40 @@ namespace Umbraco.Web.Models.Mapping
|
||||
//if already in use a composition, do not allow any composited types
|
||||
return new List<EntityBasic>();
|
||||
}
|
||||
else
|
||||
{
|
||||
// if it is not used then composition is possible
|
||||
// hashset guarantees unicity on Id
|
||||
var list = new HashSet<IContentTypeComposition>(new DelegateEqualityComparer<IContentTypeComposition>(
|
||||
(x, y) => x.Id == y.Id,
|
||||
x => x.Id));
|
||||
|
||||
// usable types are those that are top-level
|
||||
var usableContentTypes = allContentTypes
|
||||
.Where(x => x.ContentTypeComposition.Any() == false).ToArray();
|
||||
foreach (var x in usableContentTypes)
|
||||
list.Add(x);
|
||||
// if it is not used then composition is possible
|
||||
// hashset guarantees unicity on Id
|
||||
var list = new HashSet<IContentTypeComposition>(new DelegateEqualityComparer<IContentTypeComposition>(
|
||||
(x, y) => x.Id == y.Id,
|
||||
x => x.Id));
|
||||
|
||||
// indirect types are those that we use, directly or indirectly
|
||||
var indirectContentTypes = GetIndirect(source).ToArray();
|
||||
foreach (var x in indirectContentTypes)
|
||||
list.Add(x);
|
||||
// usable types are those that are top-level
|
||||
var usableContentTypes = allContentTypes
|
||||
.Where(x => x.ContentTypeComposition.Any() == false).ToArray();
|
||||
foreach (var x in usableContentTypes)
|
||||
list.Add(x);
|
||||
|
||||
// directContentTypes are those we use directly
|
||||
// they are already in indirectContentTypes, no need to add to the list
|
||||
var directContentTypes = source.ContentTypeComposition.ToArray();
|
||||
// indirect types are those that we use, directly or indirectly
|
||||
var indirectContentTypes = GetIndirect(source).ToArray();
|
||||
foreach (var x in indirectContentTypes)
|
||||
list.Add(x);
|
||||
|
||||
var enabled = usableContentTypes.Select(x => x.Id) // those we can use
|
||||
.Except(indirectContentTypes.Select(x => x.Id)) // except those that are indirectly used
|
||||
.Union(directContentTypes.Select(x => x.Id)) // but those that are directly used
|
||||
.Where(x => x != source.ParentId) // but not the parent
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
//// directContentTypes are those we use directly
|
||||
//// they are already in indirectContentTypes, no need to add to the list
|
||||
//var directContentTypes = source.ContentTypeComposition.ToArray();
|
||||
|
||||
var wtf = new List<EntityBasic>();
|
||||
foreach (var contentType in list.OrderBy(x => x.Name).Where(x => x.Id != source.Id))
|
||||
wtf.Add(Mapper.Map<IContentTypeComposition, EntityBasic>(contentType));
|
||||
//var enabled = usableContentTypes.Select(x => x.Id) // those we can use
|
||||
// .Except(indirectContentTypes.Select(x => x.Id)) // except those that are indirectly used
|
||||
// .Union(directContentTypes.Select(x => x.Id)) // but those that are directly used
|
||||
// .Where(x => x != source.ParentId) // but not the parent
|
||||
// .Distinct()
|
||||
// .ToArray();
|
||||
|
||||
return wtf;
|
||||
}
|
||||
return list
|
||||
.Where(x => x.Id != source.Id)
|
||||
.OrderBy(x => x.Name)
|
||||
.Select(Mapper.Map<IContentTypeComposition, EntityBasic>)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using AutoMapper;
|
||||
@@ -20,7 +19,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
internal class ContentTypeModelMapper : MapperConfiguration
|
||||
{
|
||||
private readonly Lazy<PropertyEditorResolver> _propertyEditorResolver;
|
||||
|
||||
|
||||
//default ctor
|
||||
public ContentTypeModelMapper()
|
||||
{
|
||||
@@ -35,207 +34,69 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
config.CreateMap<ContentTypeCompositionDisplay, IContentTypeComposition>()
|
||||
.Include<ContentTypeDisplay, IContentType>()
|
||||
.Include<ContentTypeCompositionDisplay, IMemberType>()
|
||||
.Include<ContentTypeCompositionDisplay, IMediaType>()
|
||||
|
||||
config.CreateMap<PropertyTypeBasic, PropertyType>()
|
||||
.ConstructUsing(basic => new PropertyType(applicationContext.Services.DataTypeService.GetDataTypeDefinitionById(basic.DataTypeId)))
|
||||
.ForMember(type => type.ValidationRegExp, expression => expression.ResolveUsing(basic => basic.Validation.Pattern))
|
||||
.ForMember(type => type.Mandatory, expression => expression.ResolveUsing(basic => basic.Validation.Mandatory))
|
||||
.ForMember(type => type.Name, expression => expression.ResolveUsing(basic => basic.Label))
|
||||
.ForMember(type => type.DataTypeDefinitionId, expression => expression.ResolveUsing(basic => basic.DataTypeId))
|
||||
.ForMember(type => type.DataTypeId, expression => expression.Ignore())
|
||||
.ForMember(type => type.PropertyEditorAlias, expression => expression.Ignore())
|
||||
.ForMember(type => type.HelpText, expression => expression.Ignore())
|
||||
.ForMember(type => type.Key, expression => expression.Ignore())
|
||||
.ForMember(type => type.CreateDate, expression => expression.Ignore())
|
||||
.ForMember(type => type.UpdateDate, expression => expression.Ignore())
|
||||
.ForMember(type => type.HasIdentity, expression => expression.Ignore());
|
||||
|
||||
//only map id if set to something higher then zero
|
||||
.ForMember(dto => dto.Id, expression => expression.Condition(display => (Convert.ToInt32(display.Id) > 0)))
|
||||
.ForMember(dto => dto.Id, expression => expression.MapFrom(display => Convert.ToInt32(display.Id)))
|
||||
|
||||
.ForMember(dto => dto.AllowedAsRoot, expression => expression.MapFrom(display => display.AllowAsRoot))
|
||||
.ForMember(dto => dto.CreatorId, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.Level, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.SortOrder, expression => expression.Ignore())
|
||||
|
||||
.ForMember(
|
||||
dto => dto.AllowedContentTypes,
|
||||
expression => expression.MapFrom(dto => dto.AllowedContentTypes.Select( (t, i) => new ContentTypeSort(t, i) )))
|
||||
|
||||
//ignore, we'll do this in after map
|
||||
.ForMember(dto => dto.PropertyGroups, expression => expression.Ignore())
|
||||
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
|
||||
var addedProperties = new List<string>();
|
||||
|
||||
//get all properties from groups that are not generic properties or inhertied (-666 id)
|
||||
var selfNonGenericGroups = source.Groups.Where(x => x.Inherited == false && x.Id != -666).ToArray();
|
||||
|
||||
foreach (var groupDisplay in selfNonGenericGroups)
|
||||
{
|
||||
//use underlying logic to add the property group which should wire most things up for us
|
||||
dest.AddPropertyGroup(groupDisplay.Name);
|
||||
|
||||
//now update that group with the values from the display object
|
||||
Mapper.Map(groupDisplay, dest.PropertyGroups[groupDisplay.Name]);
|
||||
|
||||
foreach (var propertyTypeDisplay in groupDisplay.Properties.Where(x => x.Inherited == false))
|
||||
{
|
||||
//update existing
|
||||
if(propertyTypeDisplay.Id > 0)
|
||||
{
|
||||
var currentPropertyType = dest.PropertyTypes.FirstOrDefault(x => x.Id == propertyTypeDisplay.Id);
|
||||
Mapper.Map(propertyTypeDisplay, currentPropertyType);
|
||||
}else
|
||||
{//add new
|
||||
var mapped = Mapper.Map<PropertyType>(propertyTypeDisplay);
|
||||
dest.AddPropertyType(mapped, groupDisplay.Name);
|
||||
}
|
||||
|
||||
addedProperties.Add(propertyTypeDisplay.Alias);
|
||||
}
|
||||
}
|
||||
|
||||
//Groups to remove
|
||||
var groupsToRemove = dest.PropertyGroups.Select(x => x.Name).Except(selfNonGenericGroups.Select(x => x.Name)).ToArray();
|
||||
foreach (var toRemove in groupsToRemove)
|
||||
{
|
||||
dest.RemovePropertyGroup(toRemove);
|
||||
}
|
||||
|
||||
//add generic properties
|
||||
var genericProperties = source.Groups.FirstOrDefault(x => x.Id == -666);
|
||||
if(genericProperties != null)
|
||||
{
|
||||
foreach (var propertyTypeDisplay in genericProperties.Properties.Where(x => x.Inherited == false))
|
||||
{
|
||||
dest.AddPropertyType(Mapper.Map<PropertyType>(propertyTypeDisplay));
|
||||
addedProperties.Add(propertyTypeDisplay.Alias);
|
||||
}
|
||||
}
|
||||
|
||||
//remove deleted types
|
||||
foreach(var removedType in dest.PropertyTypes
|
||||
.Where(x => addedProperties.Contains(x.Alias) == false).ToList())
|
||||
{
|
||||
dest.RemovePropertyType(removedType.Alias);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
config.CreateMap<ContentTypeDisplay, IContentType>()
|
||||
config.CreateMap<ContentTypeSave, IContentType>()
|
||||
//do the base mapping
|
||||
.MapBaseContentTypeSaveToEntity(applicationContext)
|
||||
.ConstructUsing((source) => new ContentType(source.ParentId))
|
||||
.ForMember(dto => dto.Id, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.AllowedTemplates, expression => expression.Ignore())
|
||||
.ForMember(source => source.AllowedTemplates, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.DefaultTemplate, expression => expression.Ignore())
|
||||
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
//sync templates
|
||||
dest.AllowedTemplates = source.AllowedTemplates.Where(x => x != null).Select(x => Mapper.Map<ITemplate>(x));
|
||||
dest.AllowedTemplates = source.AllowedTemplates
|
||||
.Where(x => x != null)
|
||||
.Select(s => applicationContext.Services.FileService.GetTemplate(s))
|
||||
.ToArray();
|
||||
|
||||
if (source.DefaultTemplate != null)
|
||||
dest.SetDefaultTemplate(Mapper.Map<ITemplate>(source.DefaultTemplate));
|
||||
dest.SetDefaultTemplate(applicationContext.Services.FileService.GetTemplate(source.DefaultTemplate));
|
||||
|
||||
|
||||
//sync compositions
|
||||
var current = dest.CompositionAliases().ToArray();
|
||||
var proposed = source.CompositeContentTypes;
|
||||
|
||||
var remove = current.Where(x => proposed.Contains(x) == false);
|
||||
var add = proposed.Where(x => current.Contains(x) == false);
|
||||
|
||||
foreach (var rem in remove)
|
||||
{
|
||||
dest.RemoveContentType(rem);
|
||||
}
|
||||
|
||||
foreach (var a in add)
|
||||
{
|
||||
|
||||
//TODO: Remove N+1 lookup
|
||||
var addCt = applicationContext.Services.ContentTypeService.GetContentType(a);
|
||||
if (addCt != null)
|
||||
dest.AddContentType(addCt);
|
||||
}
|
||||
ContentTypeModelMapperExtensions.AfterMapContentTypeSaveToEntity(source, dest, applicationContext);
|
||||
});
|
||||
|
||||
config.CreateMap<ContentTypeCompositionDisplay, IMemberType>()
|
||||
config.CreateMap<ContentTypeSave, IMediaType>()
|
||||
//do the base mapping
|
||||
.MapBaseContentTypeSaveToEntity(applicationContext)
|
||||
.ConstructUsing((source) => new MediaType(source.ParentId))
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
|
||||
//sync compositions
|
||||
var current = dest.CompositionAliases().ToArray();
|
||||
var proposed = source.CompositeContentTypes;
|
||||
{
|
||||
ContentTypeModelMapperExtensions.AfterMapContentTypeSaveToEntity(source, dest, applicationContext);
|
||||
});
|
||||
|
||||
var remove = current.Where(x => proposed.Contains(x) == false);
|
||||
var add = proposed.Where(x => current.Contains(x) == false);
|
||||
|
||||
foreach (var rem in remove)
|
||||
dest.RemoveContentType(rem);
|
||||
|
||||
foreach (var a in add)
|
||||
{
|
||||
//TODO: Remove N+1 lookup
|
||||
var addCt = applicationContext.Services.MemberTypeService.Get(a);
|
||||
if (addCt != null)
|
||||
dest.AddContentType(addCt);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
config.CreateMap<ContentTypeCompositionDisplay, IMediaType>()
|
||||
config.CreateMap<ContentTypeSave, IMemberType>()
|
||||
//do the base mapping
|
||||
.MapBaseContentTypeSaveToEntity(applicationContext)
|
||||
.ConstructUsing((source) => new MemberType(source.ParentId))
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
//sync compositions
|
||||
var current = dest.CompositionAliases().ToArray();
|
||||
var proposed = source.CompositeContentTypes;
|
||||
{
|
||||
ContentTypeModelMapperExtensions.AfterMapContentTypeSaveToEntity(source, dest, applicationContext);
|
||||
});
|
||||
|
||||
var remove = current.Where(x => proposed.Contains(x) == false);
|
||||
var add = proposed.Where(x => current.Contains(x) == false);
|
||||
|
||||
foreach (var rem in remove)
|
||||
dest.RemoveContentType(rem);
|
||||
|
||||
foreach (var a in add)
|
||||
{
|
||||
//TODO: Remove N+1 lookup
|
||||
var addCt = applicationContext.Services.ContentTypeService.GetMediaType(a);
|
||||
if (addCt != null)
|
||||
dest.AddContentType(addCt);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
config.CreateMap<IContentTypeComposition, string>().ConvertUsing(x => x.Alias);
|
||||
|
||||
config.CreateMap<IMemberType, ContentTypeCompositionDisplay>()
|
||||
//map base logic
|
||||
.MapBaseContentTypeEntityToDisplay(applicationContext, _propertyEditorResolver);
|
||||
|
||||
config.CreateMap<IContentTypeComposition, ContentTypeCompositionDisplay>()
|
||||
.Include<IContentType, ContentTypeDisplay>()
|
||||
.Include<IMemberType, ContentTypeCompositionDisplay>()
|
||||
.Include<IMediaType, ContentTypeCompositionDisplay>()
|
||||
|
||||
.ForMember(display => display.AllowAsRoot, expression => expression.MapFrom(type => type.AllowedAsRoot))
|
||||
.ForMember(display => display.ListViewEditorName, expression => expression.Ignore())
|
||||
//Ignore because this is not actually used for content types
|
||||
.ForMember(display => display.Trashed, expression => expression.Ignore())
|
||||
|
||||
.ForMember(
|
||||
dto => dto.AllowedContentTypes,
|
||||
expression => expression.MapFrom(dto => dto.AllowedContentTypes.Select(x => x.Id.Value)))
|
||||
|
||||
.ForMember(
|
||||
dto => dto.AvailableCompositeContentTypes,
|
||||
expression => expression.ResolveUsing(new AvailableCompositeContentTypesResolver(applicationContext)))
|
||||
|
||||
.ForMember(
|
||||
dto => dto.CompositeContentTypes,
|
||||
expression => expression.MapFrom(dto => dto.ContentTypeComposition))
|
||||
|
||||
.ForMember(
|
||||
dto => dto.Groups,
|
||||
expression => expression.ResolveUsing(new PropertyTypeGroupResolver(applicationContext, _propertyEditorResolver)));
|
||||
|
||||
|
||||
config.CreateMap<IMemberType, ContentTypeCompositionDisplay>();
|
||||
config.CreateMap<IMediaType, ContentTypeCompositionDisplay>()
|
||||
//map base logic
|
||||
.MapBaseContentTypeEntityToDisplay(applicationContext, _propertyEditorResolver)
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
|
||||
//default listview
|
||||
dest.ListViewEditorName = Constants.Conventions.DataTypes.ListViewPrefix + "Media";
|
||||
|
||||
@@ -248,20 +109,22 @@ namespace Umbraco.Web.Models.Mapping
|
||||
});
|
||||
|
||||
config.CreateMap<IContentType, ContentTypeDisplay>()
|
||||
//map base logic
|
||||
.MapBaseContentTypeEntityToDisplay(applicationContext, _propertyEditorResolver)
|
||||
.ForMember(dto => dto.AllowedTemplates, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.DefaultTemplate, expression => expression.Ignore())
|
||||
|
||||
.ForMember(display => display.Notifications, expression => expression.Ignore())
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
//sync templates
|
||||
dest.AllowedTemplates = source.AllowedTemplates.Select(Mapper.Map<EntityBasic>);
|
||||
dest.AllowedTemplates = source.AllowedTemplates.Select(Mapper.Map<EntityBasic>).ToArray();
|
||||
|
||||
if (source.DefaultTemplate != null)
|
||||
dest.DefaultTemplate = Mapper.Map<EntityBasic>(source.DefaultTemplate);
|
||||
|
||||
//default listview
|
||||
dest.ListViewEditorName = Constants.Conventions.DataTypes.ListViewPrefix + "Content";
|
||||
|
||||
|
||||
if (string.IsNullOrEmpty(source.Name) == false)
|
||||
{
|
||||
var name = Constants.Conventions.DataTypes.ListViewPrefix + source.Name;
|
||||
@@ -275,26 +138,13 @@ namespace Umbraco.Web.Models.Mapping
|
||||
config.CreateMap<IMediaType, ContentTypeBasic>();
|
||||
config.CreateMap<IContentType, ContentTypeBasic>();
|
||||
|
||||
config.CreateMap<PropertyTypeBasic, PropertyType>()
|
||||
|
||||
config.CreateMap<PropertyGroupDisplay, PropertyGroup>()
|
||||
.ForMember(dest => dest.Id, expression => expression.Condition(source => source.Id > 0))
|
||||
.ForMember(g => g.Key, expression => expression.Ignore())
|
||||
.ForMember(g => g.HasIdentity, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.CreateDate, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.UpdateDate, expression => expression.Ignore())
|
||||
//only map if a parent is actually set
|
||||
.ForMember(g => g.ParentId, expression => expression.Condition(display => display.ParentGroupId > 0))
|
||||
.ForMember(g => g.ParentId, expression => expression.MapFrom(display => display.ParentGroupId))
|
||||
//ignore these, this is handled with IContentType.AddPropertyType
|
||||
.ForMember(g => g.PropertyTypes, expression => expression.Ignore());
|
||||
|
||||
config.CreateMap<PropertyTypeDisplay, PropertyType>()
|
||||
|
||||
.ConstructUsing((PropertyTypeDisplay propertyTypeDisplay) =>
|
||||
.ConstructUsing((PropertyTypeBasic propertyTypeBasic) =>
|
||||
{
|
||||
var dataType = applicationContext.Services.DataTypeService.GetDataTypeDefinitionById(propertyTypeDisplay.DataTypeId);
|
||||
if (dataType == null) throw new NullReferenceException("No data type found with id " + propertyTypeDisplay.DataTypeId);
|
||||
return new PropertyType(dataType, propertyTypeDisplay.Alias);
|
||||
var dataType = applicationContext.Services.DataTypeService.GetDataTypeDefinitionById(propertyTypeBasic.DataTypeId);
|
||||
if (dataType == null) throw new NullReferenceException("No data type found with id " + propertyTypeBasic.DataTypeId);
|
||||
return new PropertyType(dataType, propertyTypeBasic.Alias);
|
||||
})
|
||||
|
||||
//only map if it is actually set
|
||||
@@ -314,11 +164,75 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(type => type.DataTypeId, expression => expression.Ignore())
|
||||
.ForMember(type => type.Mandatory, expression => expression.MapFrom(display => display.Validation.Mandatory))
|
||||
.ForMember(type => type.ValidationRegExp, expression => expression.MapFrom(display => display.Validation.Pattern))
|
||||
.ForMember(type => type.PropertyEditorAlias, expression => expression.MapFrom(display => display.Editor))
|
||||
.ForMember(type => type.DataTypeDefinitionId, expression => expression.MapFrom(display => display.DataTypeId))
|
||||
.ForMember(type => type.Name, expression => expression.MapFrom(display => display.Label));
|
||||
|
||||
#region *** Used for mapping on top of an existing display object from a save object ***
|
||||
|
||||
config.CreateMap<ContentTypeSave, ContentTypeCompositionDisplay>()
|
||||
.MapBaseContentTypeSaveToDisplay();
|
||||
|
||||
config.CreateMap<ContentTypeSave, ContentTypeDisplay>()
|
||||
.MapBaseContentTypeSaveToDisplay()
|
||||
.ForMember(dto => dto.AllowedTemplates, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.DefaultTemplate, expression => expression.Ignore())
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
//sync templates
|
||||
var destAllowedTemplateAliases = dest.AllowedTemplates.Select(x => x.Alias);
|
||||
//if the dest is set and it's the same as the source, then don't change
|
||||
if (destAllowedTemplateAliases.SequenceEqual(source.AllowedTemplates) == false)
|
||||
{
|
||||
var templates = applicationContext.Services.FileService.GetTemplates(source.AllowedTemplates.ToArray());
|
||||
dest.AllowedTemplates = source.AllowedTemplates.Select(x => Mapper.Map<EntityBasic>(templates.Single(t => t.Alias == x))).ToArray();
|
||||
}
|
||||
|
||||
if (source.DefaultTemplate.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
//if the dest is set and it's the same as the source, then don't change
|
||||
if (dest.DefaultTemplate == null || source.DefaultTemplate != dest.DefaultTemplate.Alias)
|
||||
{
|
||||
var template = applicationContext.Services.FileService.GetTemplate(source.DefaultTemplate);
|
||||
dest.DefaultTemplate = template == null ? null : Mapper.Map<EntityBasic>(template);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dest.DefaultTemplate = null;
|
||||
}
|
||||
});
|
||||
|
||||
config.CreateMap<PropertyGroupBasic<PropertyTypeBasic>, PropertyGroup>()
|
||||
.ForMember(dest => dest.Id, expression => expression.Condition(source => source.Id > 0))
|
||||
.ForMember(g => g.Key, expression => expression.Ignore())
|
||||
.ForMember(g => g.HasIdentity, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.CreateDate, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.UpdateDate, expression => expression.Ignore())
|
||||
.ForMember(g => g.ParentId, expression => expression.Ignore())
|
||||
.ForMember(g => g.PropertyTypes, expression => expression.MapFrom(basic => basic.Properties.Select(Mapper.Map<PropertyType>)));
|
||||
|
||||
config.CreateMap<PropertyGroupBasic<PropertyTypeBasic>, PropertyGroupDisplay>()
|
||||
.ForMember(dest => dest.Id, expression => expression.Condition(source => source.Id > 0))
|
||||
.ForMember(g => g.ParentGroupId, expression => expression.Ignore())
|
||||
.ForMember(g => g.ContentTypeId, expression => expression.Ignore())
|
||||
.ForMember(g => g.ParentTabContentTypes, expression => expression.Ignore())
|
||||
.ForMember(g => g.ParentTabContentTypeNames, expression => expression.Ignore())
|
||||
.ForMember(g => g.Properties, expression => expression.MapFrom(display => display.Properties.Select(Mapper.Map<PropertyTypeDisplay>)));
|
||||
|
||||
config.CreateMap<PropertyTypeBasic, PropertyTypeDisplay>()
|
||||
.ForMember(g => g.Editor, expression => expression.Ignore())
|
||||
.ForMember(g => g.View, expression => expression.Ignore())
|
||||
.ForMember(g => g.Config, expression => expression.Ignore())
|
||||
.ForMember(g => g.ContentTypeId, expression => expression.Ignore())
|
||||
.ForMember(g => g.ContentTypeName, expression => expression.Ignore());
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Used as a shared way to do the underlying mapping for content types base classes
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We used to use 'Include' Automapper inheritance functionality and although this works, the unit test
|
||||
/// to assert mappings fails which is an Automapper bug. So instead we will use an extension method for the mappings
|
||||
/// to re-use mappings.
|
||||
/// </remarks>
|
||||
internal static class ContentTypeModelMapperExtensions
|
||||
{
|
||||
|
||||
public static void AfterMapContentTypeSaveToEntity<TSource, TDestination>(
|
||||
TSource source, TDestination dest,
|
||||
ApplicationContext applicationContext)
|
||||
where TSource : ContentTypeSave
|
||||
where TDestination : IContentTypeComposition
|
||||
{
|
||||
//sync compositions
|
||||
var current = dest.CompositionAliases().ToArray();
|
||||
var proposed = source.CompositeContentTypes;
|
||||
|
||||
var remove = current.Where(x => proposed.Contains(x) == false);
|
||||
var add = proposed.Where(x => current.Contains(x) == false);
|
||||
|
||||
foreach (var rem in remove)
|
||||
{
|
||||
dest.RemoveContentType(rem);
|
||||
}
|
||||
|
||||
foreach (var a in add)
|
||||
{
|
||||
//TODO: Remove N+1 lookup
|
||||
var addCt = applicationContext.Services.ContentTypeService.GetContentType(a);
|
||||
if (addCt != null)
|
||||
dest.AddContentType(addCt);
|
||||
}
|
||||
}
|
||||
|
||||
public static IMappingExpression<TSource, TDestination> MapBaseContentTypeSaveToDisplay<TSource, TDestination>(
|
||||
this IMappingExpression<TSource, TDestination> mapping)
|
||||
where TSource : ContentTypeSave
|
||||
where TDestination : ContentTypeCompositionDisplay
|
||||
{
|
||||
return mapping
|
||||
.ForMember(dto => dto.CreateDate, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.UpdateDate, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.ListViewEditorName, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.AvailableCompositeContentTypes, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.Notifications, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.Errors, expression => expression.Ignore());
|
||||
}
|
||||
|
||||
public static IMappingExpression<TSource, TDestination> MapBaseContentTypeEntityToDisplay<TSource, TDestination>(
|
||||
this IMappingExpression<TSource, TDestination> mapping, ApplicationContext applicationContext, Lazy<PropertyEditorResolver> propertyEditorResolver)
|
||||
where TSource : IContentTypeComposition
|
||||
where TDestination : ContentTypeCompositionDisplay
|
||||
{
|
||||
return mapping
|
||||
.ForMember(display => display.Notifications, expression => expression.Ignore())
|
||||
.ForMember(display => display.Errors, expression => expression.Ignore())
|
||||
.ForMember(display => display.AllowAsRoot, expression => expression.MapFrom(type => type.AllowedAsRoot))
|
||||
.ForMember(display => display.ListViewEditorName, expression => expression.Ignore())
|
||||
//Ignore because this is not actually used for content types
|
||||
.ForMember(display => display.Trashed, expression => expression.Ignore())
|
||||
|
||||
.ForMember(
|
||||
dto => dto.AllowedContentTypes,
|
||||
expression => expression.MapFrom(dto => dto.AllowedContentTypes.Select(x => x.Id.Value)))
|
||||
|
||||
.ForMember(
|
||||
dto => dto.AvailableCompositeContentTypes,
|
||||
expression => expression.ResolveUsing(new AvailableCompositeContentTypesResolver(applicationContext)))
|
||||
|
||||
.ForMember(
|
||||
dto => dto.CompositeContentTypes,
|
||||
expression => expression.MapFrom(dto => dto.ContentTypeComposition))
|
||||
|
||||
.ForMember(
|
||||
dto => dto.Groups,
|
||||
expression => expression.ResolveUsing(new PropertyTypeGroupResolver(applicationContext, propertyEditorResolver)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display -> Entity class base mapping logic
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <typeparam name="TDestination"></typeparam>
|
||||
/// <param name="mapping"></param>
|
||||
/// <param name="applicationContext"></param>
|
||||
/// <returns></returns>
|
||||
public static IMappingExpression<TSource, TDestination> MapBaseContentTypeSaveToEntity<TSource, TDestination>(
|
||||
this IMappingExpression<TSource, TDestination> mapping, ApplicationContext applicationContext)
|
||||
//where TSource : ContentTypeCompositionDisplay
|
||||
where TSource : ContentTypeSave
|
||||
where TDestination : IContentTypeComposition
|
||||
{
|
||||
return mapping
|
||||
//only map id if set to something higher then zero
|
||||
.ForMember(dto => dto.Id, expression => expression.Condition(display => (Convert.ToInt32(display.Id) > 0)))
|
||||
.ForMember(dto => dto.Id, expression => expression.MapFrom(display => Convert.ToInt32(display.Id)))
|
||||
|
||||
//These get persisted as part of the saving procedure, nothing to do with the display model
|
||||
.ForMember(dto => dto.CreateDate, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.UpdateDate, expression => expression.Ignore())
|
||||
|
||||
.ForMember(dto => dto.AllowedAsRoot, expression => expression.MapFrom(display => display.AllowAsRoot))
|
||||
.ForMember(dto => dto.CreatorId, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.Level, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.SortOrder, expression => expression.Ignore())
|
||||
//ignore, we'll do this in after map
|
||||
.ForMember(dto => dto.PropertyGroups, expression => expression.Ignore())
|
||||
|
||||
.ForMember(
|
||||
dto => dto.AllowedContentTypes,
|
||||
expression => expression.MapFrom(dto => dto.AllowedContentTypes.Select((t, i) => new ContentTypeSort(t, i))))
|
||||
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
|
||||
var addedProperties = new List<string>();
|
||||
|
||||
//get all properties from groups that are not generic properties or inhertied (-666 id)
|
||||
var selfNonGenericGroups = source.Groups.Where(x => x.Inherited == false && x.Id != -666).ToArray();
|
||||
|
||||
foreach (var group in selfNonGenericGroups)
|
||||
{
|
||||
//use underlying logic to add the property group which should wire most things up for us
|
||||
dest.AddPropertyGroup(group.Name);
|
||||
|
||||
//now update that group with the values from the display object
|
||||
Mapper.Map(group, dest.PropertyGroups[group.Name]);
|
||||
|
||||
foreach (var propType in group.Properties.Where(x => x.Inherited == false))
|
||||
{
|
||||
//update existing
|
||||
if (propType.Id > 0)
|
||||
{
|
||||
var currentPropertyType = dest.PropertyTypes.FirstOrDefault(x => x.Id == propType.Id);
|
||||
Mapper.Map(propType, currentPropertyType);
|
||||
}
|
||||
else
|
||||
{
|
||||
//add new
|
||||
var mapped = Mapper.Map<PropertyType>(propType);
|
||||
dest.AddPropertyType(mapped, group.Name);
|
||||
}
|
||||
|
||||
addedProperties.Add(propType.Alias);
|
||||
}
|
||||
}
|
||||
|
||||
//Groups to remove
|
||||
var groupsToRemove = dest.PropertyGroups.Select(x => x.Name).Except(selfNonGenericGroups.Select(x => x.Name)).ToArray();
|
||||
foreach (var toRemove in groupsToRemove)
|
||||
{
|
||||
dest.RemovePropertyGroup(toRemove);
|
||||
}
|
||||
|
||||
//add generic properties
|
||||
var genericProperties = source.Groups.FirstOrDefault(x => x.Id == -666);
|
||||
if (genericProperties != null)
|
||||
{
|
||||
foreach (var propertyTypeBasic in genericProperties.Properties.Where(x => x.Inherited == false))
|
||||
{
|
||||
dest.AddPropertyType(Mapper.Map<PropertyType>(propertyTypeBasic));
|
||||
addedProperties.Add(propertyTypeBasic.Alias);
|
||||
}
|
||||
}
|
||||
|
||||
//remove deleted types
|
||||
foreach (var removedType in dest.PropertyTypes
|
||||
.Where(x => addedProperties.Contains(x.Alias) == false).ToList())
|
||||
{
|
||||
dest.RemovePropertyType(removedType.Alias);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
};
|
||||
|
||||
config.CreateMap<PropertyEditor, DataTypeBasic>()
|
||||
.ForMember(x => x.HasPrevalues, expression => expression.Ignore())
|
||||
.ForMember(x => x.IsSystemDataType, expression => expression.Ignore())
|
||||
.ForMember(x => x.Id, expression => expression.Ignore())
|
||||
.ForMember(x => x.Trashed, expression => expression.Ignore())
|
||||
@@ -44,6 +45,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(x => x.AdditionalData, expression => expression.Ignore());
|
||||
|
||||
config.CreateMap<IDataTypeDefinition, DataTypeBasic>()
|
||||
.ForMember(x => x.HasPrevalues, expression => expression.Ignore())
|
||||
.ForMember(x => x.Icon, expression => expression.Ignore())
|
||||
.ForMember(x => x.Alias, expression => expression.Ignore())
|
||||
.ForMember(x => x.Group, expression => expression.Ignore())
|
||||
@@ -65,6 +67,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
new PreValueDisplayResolver(lazyDataTypeService)))
|
||||
.ForMember(display => display.SelectedEditor, expression => expression.MapFrom(
|
||||
definition => definition.PropertyEditorAlias.IsNullOrWhiteSpace() ? null : definition.PropertyEditorAlias))
|
||||
.ForMember(x => x.HasPrevalues, expression => expression.Ignore())
|
||||
.ForMember(x => x.Notifications, expression => expression.Ignore())
|
||||
.ForMember(x => x.Icon, expression => expression.Ignore())
|
||||
.ForMember(x => x.Alias, expression => expression.Ignore())
|
||||
|
||||
@@ -52,18 +52,18 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(dto => dto.Trashed, expression => expression.Ignore())
|
||||
.ForMember(x => x.AdditionalData, expression => expression.Ignore());
|
||||
|
||||
config.CreateMap<EntityBasic, ITemplate>()
|
||||
.ConstructUsing(basic => new Template(basic.Name, basic.Alias)
|
||||
{
|
||||
Id = Convert.ToInt32(basic.Id),
|
||||
Key = basic.Key
|
||||
})
|
||||
.ForMember(t => t.Path, expression => expression.Ignore())
|
||||
.ForMember(t => t.Id, expression => expression.MapFrom(template => Convert.ToInt32(template.Id)))
|
||||
.ForMember(x => x.VirtualPath, expression => expression.Ignore())
|
||||
.ForMember(x => x.CreateDate, expression => expression.Ignore())
|
||||
.ForMember(x => x.UpdateDate, expression => expression.Ignore())
|
||||
.ForMember(x => x.Content, expression => expression.Ignore());
|
||||
//config.CreateMap<EntityBasic, ITemplate>()
|
||||
// .ConstructUsing(basic => new Template(basic.Name, basic.Alias)
|
||||
// {
|
||||
// Id = Convert.ToInt32(basic.Id),
|
||||
// Key = basic.Key
|
||||
// })
|
||||
// .ForMember(t => t.Path, expression => expression.Ignore())
|
||||
// .ForMember(t => t.Id, expression => expression.MapFrom(template => Convert.ToInt32(template.Id)))
|
||||
// .ForMember(x => x.VirtualPath, expression => expression.Ignore())
|
||||
// .ForMember(x => x.CreateDate, expression => expression.Ignore())
|
||||
// .ForMember(x => x.UpdateDate, expression => expression.Ignore())
|
||||
// .ForMember(x => x.Content, expression => expression.Ignore());
|
||||
|
||||
config.CreateMap<EntityBasic, ContentTypeSort>()
|
||||
.ForMember(x => x.Id, expression => expression.MapFrom(entity => new Lazy<int>(() => Convert.ToInt32(entity.Id))))
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
Label = p.Name,
|
||||
View = editor.ValueEditor.View,
|
||||
Config = editor.PreValueEditor.ConvertDbToEditor(editor.DefaultPreValues, preVals) ,
|
||||
Value = "",
|
||||
//Value = "",
|
||||
ContentTypeId = contentType.Id,
|
||||
ContentTypeName = contentType.Name,
|
||||
GroupId = groupId,
|
||||
|
||||
Reference in New Issue
Block a user