public static Lazy <IContentType> GetContentTypeDefinition(Type modelType) { //Check for BaseType different from ContentTypeBase bool hasParent = modelType.BaseType != null && modelType.BaseType != typeof(ContentTypeBase) && modelType.BaseType != typeof(object); var parent = new Lazy <IContentType>(); if (hasParent) { var isResolved = _contentTypeCache.ContainsKey(modelType.BaseType.FullName); parent = isResolved ? _contentTypeCache[modelType.BaseType.FullName].ContentType : GetContentTypeDefinition(modelType.BaseType); } var contentTypeAttribute = modelType.FirstAttribute <ContentTypeAttribute>(); var contentTypeAlias = contentTypeAttribute == null?modelType.Name.ToUmbracoAlias() : contentTypeAttribute.Alias; //Check if ContentType already exists by looking it up by Alias. var existing = ApplicationContext.Current.Services.ContentTypeService.GetContentType(contentTypeAlias); Lazy <IContentType> contentType = contentTypeAttribute == null ? PlainPocoConvention(modelType, existing) : ContentTypeConvention(contentTypeAttribute, modelType, existing); //Check for interfaces that'll be used for ContentTypeComposition var mixins = GetAliasesFromTypeInterfaces(modelType); var definitions = new List <PropertyDefinition>(); int order = 0; var objProperties = modelType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly).ToList(); foreach (var propertyInfo in objProperties) { var propertyTypeAttribute = propertyInfo.FirstAttribute <PropertyTypeConventionAttribute>(); var definition = propertyTypeAttribute == null ? new PropertyDefinition() : propertyTypeAttribute.GetPropertyConvention(); //DataTypeDefinition fallback if (definition.DataTypeDefinition == null) { definition.DataTypeDefinition = Conventions.GetDataTypeDefinitionByAttributeOrType(null, propertyInfo.PropertyType); } if (string.IsNullOrEmpty(definition.PropertyGroup)) { definition.PropertyGroup = "Generic Properties"; } //Alias fallback if (string.IsNullOrEmpty(definition.Alias)) { var aliasAttribute = propertyInfo.FirstAttribute <AliasAttribute>(); definition.Alias = Conventions.GetPropertyTypeAlias(aliasAttribute, propertyInfo.Name); definition.Name = Conventions.GetPropertyTypeName(aliasAttribute, propertyInfo.Name); } //Description fallback if (string.IsNullOrEmpty(definition.Description)) { var descriptionAttribute = propertyInfo.FirstAttribute <DescriptionAttribute>(); definition.Description = descriptionAttribute != null ? descriptionAttribute.Description : string.Empty; } //SortOrder fallback if (definition.Order == default(int)) { var sortOrderAttribute = propertyInfo.FirstAttribute <SortOrderAttribute>(); definition.Order = sortOrderAttribute != null ? sortOrderAttribute.Order : order; } definitions.Add(definition); order++; } //Loop through definitions for PropertyGroups and create those that not already exists var groupDefinitions = definitions.DistinctBy(d => d.PropertyGroup); foreach (var groupDefinition in groupDefinitions) { var groupExists = contentType.Value.PropertyGroups.Contains(groupDefinition.PropertyGroup); if (groupExists == false) { var propertyGroup = new PropertyGroup { Name = groupDefinition.PropertyGroup }; contentType.Value.PropertyGroups.Add(propertyGroup); } } //Loop through definitions for PropertyTypes and add them to the correct PropertyGroup foreach (var definition in definitions) { var group = contentType.Value.PropertyGroups.First(x => x.Name == definition.PropertyGroup); //Check if a PropertyType with the same alias already exists, as we don't want to override existing ones if (group.PropertyTypes.Contains(definition.Alias)) { continue; } var propertyType = new PropertyType(definition.DataTypeDefinition, definition.Alias) { Mandatory = definition.Mandatory, ValidationRegExp = definition.ValidationRegExp, SortOrder = definition.Order, Name = definition.Name }; group.PropertyTypes.Add(propertyType); } //If current ContentType has a Parent the ParentId should be set and the ContentType added to the composition. if (hasParent) { contentType.Value.SetLazyParentId(new Lazy <int>(() => parent.Value.Id)); contentType.Value.AddContentType(parent.Value); } //Add the resolved ContentType to the internal cache var field = new DependencyField { ContentType = contentType, Alias = contentType.Value.Alias }; var dependencies = new List <string>(); //If current type has a parent (inherited model) we add the alias of that type as a dependency if (hasParent) { dependencies.Add(parent.Value.Alias); } //Check ContentType for existing 'Allowed ContentTypes' if (contentType.Value.AllowedContentTypes.Any()) { dependencies.AddRange(contentType.Value.AllowedContentTypes.Select(allowed => allowed.Alias)); } //Check for interfaces with AliasAttribute and add those as dependencies //NOTE: might also be an idea to check if ContentType has already been created/added to cache that implements the interface. if (mixins.Any()) { foreach (var mixin in mixins) { if (dependencies.Contains(mixin.Item1)) { continue; } dependencies.Add(mixin.Item1); var isMixinResolved = _contentTypeCache.ContainsKey(mixin.Item2); Lazy <IContentType> compositionType = null; if (isMixinResolved) { compositionType = _contentTypeCache[mixin.Item2].ContentType; } else { GetContentTypeDefinition(mixin.Item3); compositionType = _contentTypeCache[mixin.Item2].ContentType; } contentType.Value.AddContentType(compositionType.Value); } } field.DependsOn = dependencies.ToArray(); _contentTypeCache.AddOrUpdate(modelType.FullName, field, (x, y) => field); return(contentType); }
public static Lazy<IContentType> GetContentTypeDefinition(Type modelType) { //Check for BaseType different from ContentTypeBase bool hasParent = modelType.BaseType != null && modelType.BaseType != typeof(ContentTypeBase) && modelType.BaseType != typeof(object); var parent = new Lazy<IContentType>(); if(hasParent) { var isResolved = _contentTypeCache.ContainsKey(modelType.BaseType.FullName); parent = isResolved ? _contentTypeCache[modelType.BaseType.FullName].ContentType : GetContentTypeDefinition(modelType.BaseType); } var contentTypeAttribute = modelType.FirstAttribute<ContentTypeAttribute>(); var contentTypeAlias = contentTypeAttribute == null ? modelType.Name.ToUmbracoAlias() : contentTypeAttribute.Alias; //Check if ContentType already exists by looking it up by Alias. var existing = ApplicationContext.Current.Services.ContentTypeService.GetContentType(contentTypeAlias); Lazy<IContentType> contentType = contentTypeAttribute == null ? PlainPocoConvention(modelType, existing) : ContentTypeConvention(contentTypeAttribute, modelType, existing); //Check for interfaces that'll be used for ContentTypeComposition var mixins = GetAliasesFromTypeInterfaces(modelType); var definitions = new List<PropertyDefinition>(); int order = 0; var objProperties = modelType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly).ToList(); foreach (var propertyInfo in objProperties) { var propertyTypeAttribute = propertyInfo.FirstAttribute<PropertyTypeConventionAttribute>(); var definition = propertyTypeAttribute == null ? new PropertyDefinition() : propertyTypeAttribute.GetPropertyConvention(); //DataTypeDefinition fallback if(definition.DataTypeDefinition == null) { definition.DataTypeDefinition = Conventions.GetDataTypeDefinitionByAttributeOrType(null, propertyInfo.PropertyType); } if(string.IsNullOrEmpty(definition.PropertyGroup)) { definition.PropertyGroup = "Generic Properties"; } //Alias fallback if (string.IsNullOrEmpty(definition.Alias)) { var aliasAttribute = propertyInfo.FirstAttribute<AliasAttribute>(); definition.Alias = Conventions.GetPropertyTypeAlias(aliasAttribute, propertyInfo.Name); definition.Name = Conventions.GetPropertyTypeName(aliasAttribute, propertyInfo.Name); } //Description fallback if (string.IsNullOrEmpty(definition.Description)) { var descriptionAttribute = propertyInfo.FirstAttribute<DescriptionAttribute>(); definition.Description = descriptionAttribute != null ? descriptionAttribute.Description : string.Empty; } //SortOrder fallback if (definition.Order == default(int)) { var sortOrderAttribute = propertyInfo.FirstAttribute<SortOrderAttribute>(); definition.Order = sortOrderAttribute != null ? sortOrderAttribute.Order : order; } definitions.Add(definition); order++; } //Loop through definitions for PropertyGroups and create those that not already exists var groupDefinitions = definitions.DistinctBy(d => d.PropertyGroup); foreach (var groupDefinition in groupDefinitions) { var groupExists = contentType.Value.PropertyGroups.Contains(groupDefinition.PropertyGroup); if(groupExists == false) { var propertyGroup = new PropertyGroup {Name = groupDefinition.PropertyGroup}; contentType.Value.PropertyGroups.Add(propertyGroup); } } //Loop through definitions for PropertyTypes and add them to the correct PropertyGroup foreach (var definition in definitions) { var group = contentType.Value.PropertyGroups.First(x => x.Name == definition.PropertyGroup); //Check if a PropertyType with the same alias already exists, as we don't want to override existing ones if(group.PropertyTypes.Contains(definition.Alias)) continue; var propertyType = new PropertyType(definition.DataTypeDefinition) { Mandatory = definition.Mandatory, ValidationRegExp = definition.ValidationRegExp, SortOrder = definition.Order, Alias = definition.Alias, Name = definition.Name }; group.PropertyTypes.Add(propertyType); } //If current ContentType has a Parent the ParentId should be set and the ContentType added to the composition. if(hasParent) { contentType.Value.SetLazyParentId(new Lazy<int>(() => parent.Value.Id)); contentType.Value.AddContentType(parent.Value); } //Add the resolved ContentType to the internal cache var field = new DependencyField {ContentType = contentType, Alias = contentType.Value.Alias}; var dependencies = new List<string>(); //If current type has a parent (inherited model) we add the alias of that type as a dependency if(hasParent) { dependencies.Add(parent.Value.Alias); } //Check ContentType for existing 'Allowed ContentTypes' if(contentType.Value.AllowedContentTypes.Any()) { dependencies.AddRange(contentType.Value.AllowedContentTypes.Select(allowed => allowed.Alias)); } //Check for interfaces with AliasAttribute and add those as dependencies //NOTE: might also be an idea to check if ContentType has already been created/added to cache that implements the interface. if(mixins.Any()) { foreach (var mixin in mixins) { if(dependencies.Contains(mixin.Item1)) continue; dependencies.Add(mixin.Item1); var isMixinResolved = _contentTypeCache.ContainsKey(mixin.Item2); Lazy<IContentType> compositionType = null; if (isMixinResolved) { compositionType = _contentTypeCache[mixin.Item2].ContentType; } else { GetContentTypeDefinition(mixin.Item3); compositionType = _contentTypeCache[mixin.Item2].ContentType; } contentType.Value.AddContentType(compositionType.Value); } } field.DependsOn = dependencies.ToArray(); _contentTypeCache.AddOrUpdate(modelType.FullName, field, (x, y) => field); return contentType; }