diff --git a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs index a71f87b001..e44f7ad2e1 100644 --- a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs @@ -13,112 +13,112 @@ namespace Umbraco.Core.Models [DataContract(IsReference = true)] public abstract class ContentTypeCompositionBase : ContentTypeBase, IContentTypeComposition { - private List _contentTypeComposition; + private List _contentTypeComposition = new List(); protected ContentTypeCompositionBase(int parentId) : base(parentId) { - _contentTypeComposition = new List(); } - private static readonly PropertyInfo ContentTypeCompositionSelector = ExpressionHelper.GetPropertyInfo>(x => x.ContentTypeComposition); + private static readonly PropertyInfo ContentTypeCompositionSelector = + ExpressionHelper.GetPropertyInfo>( + x => x.ContentTypeComposition); - /// - /// List of ContentTypes that make up a composition of PropertyGroups and PropertyTypes for the current ContentType - /// - [DataMember] - public List ContentTypeComposition - { - get { return _contentTypeComposition; } - set - { - _contentTypeComposition = value; - OnPropertyChanged(ContentTypeCompositionSelector); - } - } + /// + /// List of ContentTypes that make up a composition of PropertyGroups and PropertyTypes for the current ContentType + /// + [DataMember] + public IEnumerable ContentTypeComposition + { + get { return _contentTypeComposition; } + } - /// - /// Returns a list of objects from the composition - /// - [IgnoreDataMember] - public IEnumerable CompositionPropertyGroups - { - get - { - var groups = PropertyGroups.Union(ContentTypeComposition.SelectMany(x => x.CompositionPropertyGroups)); - return groups; - } - } + /// + /// Returns a list of objects from the composition + /// + [IgnoreDataMember] + public IEnumerable CompositionPropertyGroups + { + get + { + var groups = PropertyGroups.Union(ContentTypeComposition.SelectMany(x => x.CompositionPropertyGroups)); + return groups; + } + } - /// - /// Returns a list of objects from the composition - /// - [IgnoreDataMember] - public IEnumerable CompositionPropertyTypes - { - get - { - var propertyTypes = PropertyTypes.Union(ContentTypeComposition.SelectMany(x => x.CompositionPropertyTypes)); - return propertyTypes; - } - } + /// + /// Returns a list of objects from the composition + /// + [IgnoreDataMember] + public IEnumerable CompositionPropertyTypes + { + get + { + var propertyTypes = + PropertyTypes.Union(ContentTypeComposition.SelectMany(x => x.CompositionPropertyTypes)); + return propertyTypes; + } + } - /// - /// Adds a new ContentType to the list of composite ContentTypes - /// - /// to add - /// True if ContentType was added, otherwise returns False - public bool AddContentType(IContentTypeComposition contentType) - { - if (contentType.ContentTypeComposition.Any(x => x.CompositionAliases().Any(ContentTypeCompositionExists))) - return false; + /// + /// Adds a new ContentType to the list of composite ContentTypes + /// + /// to add + /// True if ContentType was added, otherwise returns False + public bool AddContentType(IContentTypeComposition contentType) + { + if (contentType.ContentTypeComposition.Any(x => x.CompositionAliases().Any(ContentTypeCompositionExists))) + return false; - if (!ContentTypeCompositionExists(contentType.Alias)) - { - ContentTypeComposition.Add(contentType); - return true; - } - return false; - } + if (!ContentTypeCompositionExists(contentType.Alias)) + { + _contentTypeComposition.Add(contentType); + OnPropertyChanged(ContentTypeCompositionSelector); + return true; + } + return false; + } - /// - /// Removes a ContentType with the supplied alias from the the list of composite ContentTypes - /// - /// Alias of a - /// True if ContentType was removed, otherwise returns False - public bool RemoveContentType(string alias) - { - if (!ContentTypeCompositionExists(alias)) - { - var contentTypeComposition = ContentTypeComposition.First(x => x.Alias == alias); - return ContentTypeComposition.Remove(contentTypeComposition); - } - return false; - } + /// + /// Removes a ContentType with the supplied alias from the the list of composite ContentTypes + /// + /// Alias of a + /// True if ContentType was removed, otherwise returns False + public bool RemoveContentType(string alias) + { + if (!ContentTypeCompositionExists(alias)) + { + var contentTypeComposition = ContentTypeComposition.First(x => x.Alias == alias); + return _contentTypeComposition.Remove(contentTypeComposition); + } + return false; + } - /// - /// Checks if a ContentType with the supplied alias exists in the list of composite ContentTypes - /// - /// Alias of a - /// True if ContentType with alias exists, otherwise returns False - public bool ContentTypeCompositionExists(string alias) - { - if (ContentTypeComposition.Any(x => x.Alias.Equals(alias))) - return true; + /// + /// Checks if a ContentType with the supplied alias exists in the list of composite ContentTypes + /// + /// Alias of a + /// True if ContentType with alias exists, otherwise returns False + public bool ContentTypeCompositionExists(string alias) + { + if (ContentTypeComposition.Any(x => x.Alias.Equals(alias))) + return true; - if (ContentTypeComposition.Any(x => x.ContentTypeCompositionExists(alias))) - return true; + if (ContentTypeComposition.Any(x => x.ContentTypeCompositionExists(alias))) + return true; - return false; - } + return false; + } - /// - /// Gets a list of ContentType aliases from the current composition - /// - /// - /// Does not contain the alias of the Current ContentType - public IEnumerable CompositionAliases() - { - return ContentTypeComposition.Select(x => x.Alias).Union(ContentTypeComposition.SelectMany(x => x.CompositionAliases())); - } + /// + /// Gets a list of ContentType aliases from the current composition + /// + /// + /// Does not contain the alias of the Current ContentType + public IEnumerable CompositionAliases() + { + return ContentTypeComposition + .Select(x => x.Alias) + .Union(ContentTypeComposition.SelectMany(x => x.CompositionAliases())); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/IContentTypeComposition.cs b/src/Umbraco.Core/Models/IContentTypeComposition.cs index 5e97d75e6c..6da7f2e9c9 100644 --- a/src/Umbraco.Core/Models/IContentTypeComposition.cs +++ b/src/Umbraco.Core/Models/IContentTypeComposition.cs @@ -10,7 +10,7 @@ namespace Umbraco.Core.Models /// /// Gets a list of ContentTypes that make up a composition of PropertyGroups and PropertyTypes for the current ContentType /// - List ContentTypeComposition { get; } + IEnumerable ContentTypeComposition { get; } /// /// Gets a list of objects from the composition diff --git a/src/Umbraco.Core/Models/Rdbms/ContentType2ContentTypeDto.cs b/src/Umbraco.Core/Models/Rdbms/ContentType2ContentTypeDto.cs index 132181099a..89773ef1ee 100644 --- a/src/Umbraco.Core/Models/Rdbms/ContentType2ContentTypeDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/ContentType2ContentTypeDto.cs @@ -9,9 +9,11 @@ namespace Umbraco.Core.Models.Rdbms { [Column("parentContentTypeId")] [PrimaryKeyColumn(AutoIncrement = false, Clustered = true, Name = "PK_cmsContentType2ContentType", OnColumns = "parentContentTypeId, childContentTypeId")] + [ForeignKey(typeof(NodeDto), Name = "FK_cmsContentType2ContentType_umbracoNode_parent")] public int ParentId { get; set; } [Column("childContentTypeId")] + [ForeignKey(typeof(NodeDto), Name = "FK_cmsContentType2ContentType_umbracoNode_child")] public int ChildId { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/DataTypePreValueDto.cs b/src/Umbraco.Core/Models/Rdbms/DataTypePreValueDto.cs index e6666ea6f0..4f19d60abd 100644 --- a/src/Umbraco.Core/Models/Rdbms/DataTypePreValueDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/DataTypePreValueDto.cs @@ -13,7 +13,7 @@ namespace Umbraco.Core.Models.Rdbms public int Id { get; set; } [Column("datatypeNodeId")] - [ForeignKey(typeof(DataTypeDto))] + [ForeignKey(typeof(DataTypeDto), Column = "nodeId")] public int DataTypeNodeId { get; set; } [Column("value")] diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index 451d105d3a..818dec4f49 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -84,7 +84,19 @@ namespace Umbraco.Core.Persistence.Repositories { if (composition.Id == entity.Id) continue;//Just to ensure that we aren't creating a reference to ourself. - Database.Insert(new ContentType2ContentTypeDto { ParentId = composition.Id, ChildId = entity.Id }); + if (composition.HasIdentity) + { + Database.Insert(new ContentType2ContentTypeDto { ParentId = composition.Id, ChildId = entity.Id }); + } + else + { + //Fallback for ContentTypes with no identity + var contentTypeDto = Database.FirstOrDefault("WHERE alias = @Alias", new {Alias = composition.Alias}); + if (contentTypeDto != null) + { + Database.Insert(new ContentType2ContentTypeDto { ParentId = contentTypeDto.NodeId, ChildId = entity.Id }); + } + } } //Insert collection of allowed content types diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index 4b2fa2edaa..00759eac24 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Querying; @@ -126,6 +127,8 @@ namespace Umbraco.Core.Services content.Value.CreatorId = 0; repository.AddOrUpdate(content.Value); _unitOfWork.Commit(); + + LogHelper.Info(string.Format("Saved ContentType with Alias '{0}' and Id '{1}'", content.Value.Alias, content.Value.Id)); } } diff --git a/src/Umbraco.Tests/CodeFirst/Attributes/MixinAttribute.cs b/src/Umbraco.Tests/CodeFirst/Attributes/MixinAttribute.cs new file mode 100644 index 0000000000..eafb67f76c --- /dev/null +++ b/src/Umbraco.Tests/CodeFirst/Attributes/MixinAttribute.cs @@ -0,0 +1,18 @@ +using System; + +namespace Umbraco.Tests.CodeFirst.Attributes +{ + [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = true)] + public class MixinAttribute : Attribute + { + public MixinAttribute(Type type) + { + Type = type; + } + + /// + /// Gets or sets the Type of the implementing class + /// + public Type Type { get; private set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/CodeFirst/Attributes/Numeric.cs b/src/Umbraco.Tests/CodeFirst/Attributes/Numeric.cs index b46eedb78e..9c98033b13 100644 --- a/src/Umbraco.Tests/CodeFirst/Attributes/Numeric.cs +++ b/src/Umbraco.Tests/CodeFirst/Attributes/Numeric.cs @@ -30,8 +30,8 @@ namespace Umbraco.Tests.CodeFirst.Attributes if(string.IsNullOrEmpty(PreValue) == false) { - Conventions.CreatePrevalueForDataTypeDefinition(definition.DataTypeDefinition.Id, PreValue, 0, - string.Empty); + //TODO - test inserting a prevalue when a DataTypeDefinition has been created, as its currently throwing a foreignkey constraint error. + Conventions.CreatePrevalueForDataTypeDefinition(definition.DataTypeDefinition.Id, PreValue, 0, string.Empty); } return definition; diff --git a/src/Umbraco.Tests/CodeFirst/CodeFirstTests.cs b/src/Umbraco.Tests/CodeFirst/CodeFirstTests.cs index 631efb34b4..16e2106d32 100644 --- a/src/Umbraco.Tests/CodeFirst/CodeFirstTests.cs +++ b/src/Umbraco.Tests/CodeFirst/CodeFirstTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; using NUnit.Framework; @@ -10,6 +9,7 @@ using Umbraco.Core.ObjectResolution; using Umbraco.Core.Serialization; using Umbraco.Tests.CodeFirst.Definitions; using Umbraco.Tests.CodeFirst.TestModels; +using Umbraco.Tests.CodeFirst.TestModels.Composition; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; using umbraco.editorControls.tinyMCE3; @@ -32,7 +32,8 @@ namespace Umbraco.Tests.CodeFirst PluginManager.Current.AssembliesToScan = new[] { typeof(IDataType).Assembly, - typeof(tinyMCE3dataType).Assembly + typeof(tinyMCE3dataType).Assembly, + typeof (ContentTypeBase).Assembly }; DataTypesResolver.Current = new DataTypesResolver( @@ -187,37 +188,43 @@ namespace Umbraco.Tests.CodeFirst } - private SerializationService SerializationService { get; set; } - - private static int[] GetTopologicalSortOrder(IList fields) + [Test] + public void Can_Resolve_ContentType_Composition_And_Save_To_Database() { - var g = new TopologicalSorter(fields.Count()); - var _indexes = new Dictionary(); + ContentTypeDefinitionFactory.ClearContentTypeCache(); - //add vertices - for (int i = 0; i < fields.Count(); i++) - { - _indexes[fields[i].Alias.ToLower()] = g.AddVertex(i); - } + var metaSeoModel = typeof(MetaSeo); + var seoContentType = ContentTypeDefinitionFactory.GetContentTypeDefinition(metaSeoModel); + var metaModel = typeof(Meta); + var metaContentType = ContentTypeDefinitionFactory.GetContentTypeDefinition(metaModel); + var baseModel = typeof(Base); + var baseContentType = ContentTypeDefinitionFactory.GetContentTypeDefinition(baseModel); + var newsModel = typeof(News); + var newsContentType = ContentTypeDefinitionFactory.GetContentTypeDefinition(newsModel); - //add edges - for (int i = 0; i < fields.Count; i++) - { - if (fields[i].DependsOn != null) - { - for (int j = 0; j < fields[i].DependsOn.Length; j++) - { - g.AddEdge(i, - _indexes[fields[i].DependsOn[j].ToLower()]); - } - } - } - - int[] result = g.Sort(); - return result; + var mappedContentTypes = ContentTypeDefinitionFactory.RetrieveMappedContentTypes().ToList(); + ServiceContext.ContentTypeService.Save(mappedContentTypes); + Assert.That(mappedContentTypes.Count(), Is.EqualTo(4)); } + [Test] + public void Can_Resolve_Full_List_Of_Models_Implementing_ContentTypeBase() + { + ContentTypeDefinitionFactory.ClearContentTypeCache(); + + var foundTypes = PluginManager.Current.ResolveContentTypeBaseTypes(); + var contentTypeList = foundTypes.Select(ContentTypeDefinitionFactory.GetContentTypeDefinition).ToList(); + + var mappedContentTypes = ContentTypeDefinitionFactory.RetrieveMappedContentTypes(); + + Assert.That(contentTypeList.Count(), Is.EqualTo(mappedContentTypes.Count())); + + ServiceContext.ContentTypeService.Save(mappedContentTypes);//Save to db + } + + private SerializationService SerializationService { get; set; } + [TearDown] public override void TearDown() { diff --git a/src/Umbraco.Tests/CodeFirst/Definitions/ContentTypeDefinitionFactory.cs b/src/Umbraco.Tests/CodeFirst/Definitions/ContentTypeDefinitionFactory.cs index cdeff7ce62..97c138606a 100644 --- a/src/Umbraco.Tests/CodeFirst/Definitions/ContentTypeDefinitionFactory.cs +++ b/src/Umbraco.Tests/CodeFirst/Definitions/ContentTypeDefinitionFactory.cs @@ -37,6 +37,9 @@ namespace Umbraco.Tests.CodeFirst.Definitions ? PlainPocoConvention(modelType, existing) : ContentTypeConvention(contentTypeAttribute, modelType, existing); + //Check for interfaces that'll be used for ContentTypeComposition + var mixins = GetAliasesFromTypeInterfaces(modelType); + var definitions = new List(); int order = 0; var objProperties = modelType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly).ToList(); @@ -126,17 +129,48 @@ namespace Umbraco.Tests.CodeFirst.Definitions //Add the resolved ContentType to the internal cache var field = new DependencyField {ContentType = contentType, Alias = contentType.Value.Alias}; var dependencies = new List(); + //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 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; } + private static int[] GetTopologicalSortOrder(IList fields) { var g = new TopologicalSorter(fields.Count()); @@ -191,6 +225,37 @@ namespace Umbraco.Tests.CodeFirst.Definitions _contentTypeCache.Clear(); } + /// + /// Retrieves a list of aliases that correspond to the interfaces on the passed in type, + /// which are attributed with the AliasAttribute. + /// + /// + /// The modelType is also used to ensure that the implementing class doesn't reference + /// itself via its interface. + /// + /// Type of the model to retrieve interface aliases from + /// An enumerable list of strings with aliases + private static IEnumerable> GetAliasesFromTypeInterfaces(Type modelType) + { + var interfaces = modelType.GetInterfaces().ToList().Distinct(); + var list = new List>(); + + foreach (var interfaceType in interfaces) + { + var mixinAttribute = interfaceType.FirstAttribute(); + if (mixinAttribute != null) + { + if(mixinAttribute.Type == modelType) continue; + var contentTypeAttribute = mixinAttribute.Type.FirstAttribute(); + var contentTypeAlias = contentTypeAttribute == null ? mixinAttribute.Type.Name.ToUmbracoAlias() : contentTypeAttribute.Alias; + var tuple = new Tuple(contentTypeAlias, mixinAttribute.Type.FullName, mixinAttribute.Type); + list.Add(tuple); + } + } + + return list; + } + /// /// Convention that converts a class decorated with the ContentTypeAttribute to an initial ContentType /// diff --git a/src/Umbraco.Tests/CodeFirst/PluginManagerExtensions.cs b/src/Umbraco.Tests/CodeFirst/PluginManagerExtensions.cs new file mode 100644 index 0000000000..23e66b3e8f --- /dev/null +++ b/src/Umbraco.Tests/CodeFirst/PluginManagerExtensions.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core; + +namespace Umbraco.Tests.CodeFirst +{ + /// + /// Used for PluginTypeResolverTests + /// + internal static class PluginManagerExtensions + { + public static IEnumerable ResolveContentTypeBaseTypes(this PluginManager resolver) + { + return resolver.ResolveTypes(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/CodeFirst/TestModels/Composition/Base.cs b/src/Umbraco.Tests/CodeFirst/TestModels/Composition/Base.cs index c45ef1f299..c5193a8f28 100644 --- a/src/Umbraco.Tests/CodeFirst/TestModels/Composition/Base.cs +++ b/src/Umbraco.Tests/CodeFirst/TestModels/Composition/Base.cs @@ -1,10 +1,14 @@ -namespace Umbraco.Tests.CodeFirst.TestModels.Composition +using Umbraco.Tests.CodeFirst.Attributes; + +namespace Umbraco.Tests.CodeFirst.TestModels.Composition { - public class Base : IBase + public class Base : ContentTypeBase, IBase { - + [Richtext(PropertyGroup = "Content")] + public string BodyContent { get; set; } } + [Mixin(typeof(Base))] public interface IBase { diff --git a/src/Umbraco.Tests/CodeFirst/TestModels/Composition/Meta.cs b/src/Umbraco.Tests/CodeFirst/TestModels/Composition/Meta.cs index 5d0d0f298f..d15481df28 100644 --- a/src/Umbraco.Tests/CodeFirst/TestModels/Composition/Meta.cs +++ b/src/Umbraco.Tests/CodeFirst/TestModels/Composition/Meta.cs @@ -1,13 +1,19 @@ using Umbraco.Tests.CodeFirst.Attributes; +using umbraco.editorControls.textfield; +using umbraco.editorControls.textfieldmultiple; namespace Umbraco.Tests.CodeFirst.TestModels.Composition { - public class Meta : IMeta + public class Meta : ContentTypeBase, IMeta { - + [PropertyType(typeof(TextFieldDataType), PropertyGroup = "Content")] + public string MetaKeywords { get; set; } + + [PropertyType(typeof(textfieldMultipleDataType))] + public string MetaDescription { get; set; } } - [Alias("meta", Name = "Meta")] + [Mixin(typeof(Meta))] public interface IMeta {} } \ No newline at end of file diff --git a/src/Umbraco.Tests/CodeFirst/TestModels/Composition/MetaSeo.cs b/src/Umbraco.Tests/CodeFirst/TestModels/Composition/MetaSeo.cs new file mode 100644 index 0000000000..1708222053 --- /dev/null +++ b/src/Umbraco.Tests/CodeFirst/TestModels/Composition/MetaSeo.cs @@ -0,0 +1,15 @@ +using Umbraco.Tests.CodeFirst.Attributes; +using umbraco.editorControls.textfieldmultiple; + +namespace Umbraco.Tests.CodeFirst.TestModels.Composition +{ + public class MetaSeo : ContentTypeBase, IMetaSeo + { + [PropertyType(typeof(textfieldMultipleDataType))] + public string FriendlySeoStuff { get; set; } + } + + [Mixin(typeof(MetaSeo))] + public interface IMetaSeo + {} +} \ No newline at end of file diff --git a/src/Umbraco.Tests/CodeFirst/TestModels/Composition/News.cs b/src/Umbraco.Tests/CodeFirst/TestModels/Composition/News.cs index d0cfc47a40..766b9f002d 100644 --- a/src/Umbraco.Tests/CodeFirst/TestModels/Composition/News.cs +++ b/src/Umbraco.Tests/CodeFirst/TestModels/Composition/News.cs @@ -1,10 +1,14 @@ -namespace Umbraco.Tests.CodeFirst.TestModels.Composition +using Umbraco.Tests.CodeFirst.Attributes; +using umbraco.editorControls.textfield; + +namespace Umbraco.Tests.CodeFirst.TestModels.Composition { /// /// Deriving class is parent, interfaces are compositions /// - public class News : Base, IMeta, ISeo + public class News : Base, IMetaSeo, IMeta { - + [PropertyType(typeof(TextFieldDataType), PropertyGroup = "Content")] + public string Author { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/CodeFirst/TestModels/Composition/Seo.cs b/src/Umbraco.Tests/CodeFirst/TestModels/Composition/Seo.cs deleted file mode 100644 index c51756fc77..0000000000 --- a/src/Umbraco.Tests/CodeFirst/TestModels/Composition/Seo.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Umbraco.Tests.CodeFirst.Attributes; - -namespace Umbraco.Tests.CodeFirst.TestModels.Composition -{ - public class Seo : ISeo - { - - } - - [Alias("seo", Name = "Seo")] - public interface ISeo : IBase - {} -} \ No newline at end of file diff --git a/src/Umbraco.Tests/CodeFirst/TestModels/DecoratedModelPage.cs b/src/Umbraco.Tests/CodeFirst/TestModels/DecoratedModelPage.cs index c3e85835e6..2fa69e7d4d 100644 --- a/src/Umbraco.Tests/CodeFirst/TestModels/DecoratedModelPage.cs +++ b/src/Umbraco.Tests/CodeFirst/TestModels/DecoratedModelPage.cs @@ -7,7 +7,7 @@ namespace Umbraco.Tests.CodeFirst.TestModels [ContentType("modelPage", AllowedChildContentTypes = new[] { typeof(ContentPage) }, AllowedTemplates = new[]{"umbMaster"})] - public class DecoratedModelPage + public class DecoratedModelPage : ContentTypeBase { [PropertyType(typeof(TextFieldDataType), PropertyGroup = "Content")] public string Author { get; set; } diff --git a/src/Umbraco.Tests/CodeFirst/TestModels/Home.cs b/src/Umbraco.Tests/CodeFirst/TestModels/Home.cs index e32cd572e3..ffed8cf6c3 100644 --- a/src/Umbraco.Tests/CodeFirst/TestModels/Home.cs +++ b/src/Umbraco.Tests/CodeFirst/TestModels/Home.cs @@ -5,7 +5,7 @@ using umbraco.editorControls.textfieldmultiple; namespace Umbraco.Tests.CodeFirst.TestModels { [ContentType("home", AllowedChildContentTypes = new[] { typeof(ContentPage) })] - public class Home + public class Home : ContentTypeBase { [PropertyType(typeof(TextFieldDataType))] public string SiteName { get; set; } diff --git a/src/Umbraco.Tests/CodeFirst/TestModels/ModelWithNewDataType.cs b/src/Umbraco.Tests/CodeFirst/TestModels/ModelWithNewDataType.cs index 2b3945b432..948b76c493 100644 --- a/src/Umbraco.Tests/CodeFirst/TestModels/ModelWithNewDataType.cs +++ b/src/Umbraco.Tests/CodeFirst/TestModels/ModelWithNewDataType.cs @@ -4,7 +4,7 @@ using umbraco.editorControls.tinymce; namespace Umbraco.Tests.CodeFirst.TestModels { - public class ModelWithNewDataType + public class ModelWithNewDataType : ContentTypeBase { [PropertyType(typeof(TextFieldDataType), PropertyGroup = "Content")] public string Title { get; set; } diff --git a/src/Umbraco.Tests/CodeFirst/TestModels/NumericModel.cs b/src/Umbraco.Tests/CodeFirst/TestModels/NumericModel.cs index 743cbf64d6..0927d7dcb7 100644 --- a/src/Umbraco.Tests/CodeFirst/TestModels/NumericModel.cs +++ b/src/Umbraco.Tests/CodeFirst/TestModels/NumericModel.cs @@ -2,7 +2,7 @@ namespace Umbraco.Tests.CodeFirst.TestModels { - public class NumericModel + public class NumericModel : ContentTypeBase { [Numeric("Number DataType", PreValue = "5", PropertyGroup = "Content")] public int Number { get; set; } diff --git a/src/Umbraco.Tests/CodeFirst/TestModels/PlainPocoType.cs b/src/Umbraco.Tests/CodeFirst/TestModels/PlainPocoType.cs index 27591a522d..38439d8a0e 100644 --- a/src/Umbraco.Tests/CodeFirst/TestModels/PlainPocoType.cs +++ b/src/Umbraco.Tests/CodeFirst/TestModels/PlainPocoType.cs @@ -2,17 +2,22 @@ namespace Umbraco.Tests.CodeFirst.TestModels { - //Plain Poco Type - plainPocoType - public class PlainPocoType + //Name: Plain Poco Type - Alias: plainPocoType + public class PlainPocoType : ContentTypeBase { + //Name: Title, Alias: title, DataType: Text Field public string Title { get; set; } + //Name: Author, Alias: author, DataType: Text Field public string Author { get; set; } + //Name: Is Finished, Alias: isFinished, DataType: Yes/No public bool IsFinished { get; set; } + //Name: Weight, Alias: weight, DataType: Number public int Weight { get; set; } + //Name: Publish Date, Alias: publishDate, DataType: Datepicker public DateTime PublishDate { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/CodeFirst/TestModels/SimpleContentPage.cs b/src/Umbraco.Tests/CodeFirst/TestModels/SimpleContentPage.cs index 8cabbbed40..aa26795b8a 100644 --- a/src/Umbraco.Tests/CodeFirst/TestModels/SimpleContentPage.cs +++ b/src/Umbraco.Tests/CodeFirst/TestModels/SimpleContentPage.cs @@ -3,7 +3,7 @@ using umbraco.editorControls.textfield; namespace Umbraco.Tests.CodeFirst.TestModels { - public class SimpleContentPage + public class SimpleContentPage : ContentTypeBase { [PropertyType(typeof(TextFieldDataType), PropertyGroup = "Content")] public string Title { get; set; } diff --git a/src/Umbraco.Tests/CodeFirst/TestModels/TextPage.cs b/src/Umbraco.Tests/CodeFirst/TestModels/TextPage.cs index 48ffc188da..923c8495c7 100644 --- a/src/Umbraco.Tests/CodeFirst/TestModels/TextPage.cs +++ b/src/Umbraco.Tests/CodeFirst/TestModels/TextPage.cs @@ -3,7 +3,7 @@ using umbraco.editorControls.textfield; namespace Umbraco.Tests.CodeFirst.TestModels { - public class TextPage + public class TextPage : ContentTypeBase { [PropertyType(typeof(TextFieldDataType), PropertyGroup = "Content")] public string Author { get; set; } diff --git a/src/Umbraco.Tests/CodeFirst/TypeInheritanceTest.cs b/src/Umbraco.Tests/CodeFirst/TypeInheritanceTest.cs index 56ca19d999..70b6bda579 100644 --- a/src/Umbraco.Tests/CodeFirst/TypeInheritanceTest.cs +++ b/src/Umbraco.Tests/CodeFirst/TypeInheritanceTest.cs @@ -1,24 +1,82 @@ -using System.Linq; +using System; +using System.Collections.Generic; +using System.Linq; using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Tests.CodeFirst.Attributes; +using Umbraco.Tests.CodeFirst.Definitions; using Umbraco.Tests.CodeFirst.TestModels.Composition; +using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.CodeFirst { [TestFixture] public class TypeInheritanceTest { + [SetUp] + public void Initialize() + { + TestHelper.SetupLog4NetForTests(); + + //this ensures its reset + PluginManager.Current = new PluginManager(false); + + //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver + PluginManager.Current.AssembliesToScan = new[] + { + typeof (ContentTypeBase).Assembly + }; + } + [Test] public void Can_Get_Interfaces_From_Type() { var type = typeof (News); var interfaces = type.GetInterfaces().ToList(); - bool hasSeo = interfaces.Any(x => x.Name == typeof(ISeo).Name); + bool hasSeo = interfaces.Any(x => x.Name == typeof(IMetaSeo).Name); bool hasMeta = interfaces.Any(x => x.Name == typeof(IMeta).Name); Assert.That(hasSeo, Is.True); Assert.That(hasMeta, Is.True); Assert.That(interfaces.Count, Is.EqualTo(3)); } + + [Test] + public void Can_Get_MixinAttribute_From_Types() + { + var type = typeof(News); + var interfaces = type.GetInterfaces().ToList(); + + var list = new List(); + + foreach (var interfaceType in interfaces) + { + var mixinAttribute = interfaceType.FirstAttribute(); + if(mixinAttribute != null) + { + var modelType = mixinAttribute.Type; + var contentTypeAttribute = modelType.FirstAttribute(); + var contentTypeAlias = contentTypeAttribute == null ? modelType.Name.ToUmbracoAlias() : contentTypeAttribute.Alias; + list.Add(contentTypeAlias); + } + } + + Assert.That(list.Count, Is.EqualTo(2)); + Assert.That(list.Any(x => x == "meta"), Is.True); + Assert.That(list.Any(x => x == "seo"), Is.True); + } + + [Test] + public void Ensure_Only_One_Type_List_Created() + { + var foundTypes = PluginManager.Current.ResolveContentTypeBaseTypes(); + + Assert.That(foundTypes.Count(), Is.EqualTo(13)); + Assert.AreEqual(1, + PluginManager.Current.GetTypeLists() + .Count(x => x.IsTypeList(PluginManager.TypeResolutionKind.FindAllTypes))); + } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 9eb9f3eb20..1cfbcfb9c7 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -101,6 +101,7 @@ + @@ -113,11 +114,12 @@ + - +