diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index 8ef9f1ca8f..f88b29c149 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -177,7 +177,7 @@ namespace Umbraco.Core { { Comments, - new PropertyType(new Guid(PropertyEditors.TextboxMultiple), DataTypeDatabaseType.Ntext) + new PropertyType(new Guid(PropertyEditors.TextboxMultiple), DataTypeDatabaseType.Ntext, true) { Alias = Comments, Name = CommentsLabel @@ -185,7 +185,7 @@ namespace Umbraco.Core }, { FailedPasswordAttempts, - new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Integer) + new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Integer, true) { Alias = FailedPasswordAttempts, Name = FailedPasswordAttemptsLabel @@ -193,7 +193,7 @@ namespace Umbraco.Core }, { IsApproved, - new PropertyType(new Guid(PropertyEditors.TrueFalse), DataTypeDatabaseType.Integer) + new PropertyType(new Guid(PropertyEditors.TrueFalse), DataTypeDatabaseType.Integer, true) { Alias = IsApproved, Name = IsApprovedLabel @@ -201,7 +201,7 @@ namespace Umbraco.Core }, { IsLockedOut, - new PropertyType(new Guid(PropertyEditors.TrueFalse), DataTypeDatabaseType.Integer) + new PropertyType(new Guid(PropertyEditors.TrueFalse), DataTypeDatabaseType.Integer, true) { Alias = IsLockedOut, Name = IsLockedOutLabel @@ -209,7 +209,7 @@ namespace Umbraco.Core }, { LastLockoutDate, - new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Date) + new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Date, true) { Alias = LastLockoutDate, Name = LastLockoutDateLabel @@ -217,7 +217,7 @@ namespace Umbraco.Core }, { LastLoginDate, - new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Date) + new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Date, true) { Alias = LastLoginDate, Name = LastLoginDateLabel @@ -225,7 +225,7 @@ namespace Umbraco.Core }, { LastPasswordChangeDate, - new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Date) + new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Date, true) { Alias = LastPasswordChangeDate, Name = LastPasswordChangeDateLabel @@ -233,7 +233,7 @@ namespace Umbraco.Core }, { PasswordAnswer, - new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Nvarchar) + new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Nvarchar, true) { Alias = PasswordAnswer, Name = PasswordAnswerLabel @@ -241,7 +241,7 @@ namespace Umbraco.Core }, { PasswordQuestion, - new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Nvarchar) + new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Nvarchar, true) { Alias = PasswordQuestion, Name = PasswordQuestionLabel diff --git a/src/Umbraco.Core/DateTimeExtensions.cs b/src/Umbraco.Core/DateTimeExtensions.cs index 74460814b3..1ff996170f 100644 --- a/src/Umbraco.Core/DateTimeExtensions.cs +++ b/src/Umbraco.Core/DateTimeExtensions.cs @@ -18,5 +18,30 @@ namespace Umbraco.Core return dt.ToString("yyyy-MM-dd HH:mm:ss"); } + public static DateTime TruncateTo(this DateTime dt, DateTruncate truncateTo) + { + if (truncateTo == DateTruncate.Year) + return new DateTime(dt.Year, 0, 0); + if (truncateTo == DateTruncate.Month) + return new DateTime(dt.Year, dt.Month, 0); + if (truncateTo == DateTruncate.Day) + return new DateTime(dt.Year, dt.Month, dt.Day); + if (truncateTo == DateTruncate.Hour) + return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, 0, 0); + if (truncateTo == DateTruncate.Minute) + return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0); + return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second); + } + + public enum DateTruncate + { + Year, + Month, + Day, + Hour, + Minute, + Second + } + } } diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs index d6298d9da2..782d5f4bf4 100644 --- a/src/Umbraco.Core/Models/Property.cs +++ b/src/Umbraco.Core/Models/Property.cs @@ -43,7 +43,7 @@ namespace Umbraco.Core.Models private static readonly PropertyInfo ValueSelector = ExpressionHelper.GetPropertyInfo(x => x.Value); private static readonly PropertyInfo VersionSelector = ExpressionHelper.GetPropertyInfo(x => x.Version); - + /// /// Returns the Alias of the PropertyType, which this Property is based on /// @@ -59,9 +59,14 @@ namespace Umbraco.Core.Models /// /// Returns the DatabaseType that the underlaying DataType is using to store its values /// - /// Only used internally when saving the property value + /// + /// Only used internally when saving the property value. + /// [IgnoreDataMember] - internal DataTypeDatabaseType DataTypeDatabaseType { get { return _propertyType.DataTypeDatabaseType; } } + internal DataTypeDatabaseType DataTypeDatabaseType + { + get { return _propertyType.DataTypeDatabaseType; } + } /// /// Returns the PropertyType, which this Property is based on diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index cfeb9e832d..b312cc98f5 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -14,6 +14,7 @@ namespace Umbraco.Core.Models [DataContract(IsReference = true)] public class PropertyType : Entity, IEquatable { + private readonly bool _isExplicitDbType; private string _name; private string _alias; private string _description; @@ -29,16 +30,28 @@ namespace Umbraco.Core.Models public PropertyType(IDataTypeDefinition dataTypeDefinition) { if(dataTypeDefinition.HasIdentity) - DataTypeDefinitionId = dataTypeDefinition.Id; + _dataTypeDefinitionId = dataTypeDefinition.Id; - DataTypeId = dataTypeDefinition.ControlId; - DataTypeDatabaseType = dataTypeDefinition.DatabaseType; + _dataTypeId = dataTypeDefinition.ControlId; + _dataTypeDatabaseType = dataTypeDefinition.DatabaseType; } internal PropertyType(Guid dataTypeControlId, DataTypeDatabaseType dataTypeDatabaseType) + : this(dataTypeControlId, dataTypeDatabaseType, false) { - DataTypeId = dataTypeControlId; - DataTypeDatabaseType = dataTypeDatabaseType; + } + + /// + /// Used internally to assign an explicity database type for this property type regardless of what the underlying data type/property editor is. + /// + /// + /// + /// + internal PropertyType(Guid dataTypeControlId, DataTypeDatabaseType dataTypeDatabaseType, bool isExplicitDbType) + { + _isExplicitDbType = isExplicitDbType; + _dataTypeId = dataTypeControlId; + _dataTypeDatabaseType = dataTypeDatabaseType; } private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); @@ -149,6 +162,9 @@ namespace Umbraco.Core.Models get { return _dataTypeDatabaseType; } set { + //don't allow setting this if an explicit declaration has been made in the ctor + if (_isExplicitDbType) return; + SetPropertyValueAndDetectChanges(o => { _dataTypeDatabaseType = value; diff --git a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs index e291916e10..b6b3425425 100644 --- a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs @@ -11,6 +11,8 @@ namespace Umbraco.Core.Persistence.Factories { public IMemberType BuildEntity(MemberTypeReadOnlyDto dto) { + var standardPropertyTypes = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); + var memberType = new MemberType(dto.ParentId) { Alias = dto.Alias, @@ -32,13 +34,12 @@ namespace Umbraco.Core.Persistence.Factories AllowedContentTypes = Enumerable.Empty() }; - var propertyTypeGroupCollection = GetPropertyTypeGroupCollection(dto, memberType); + var propertyTypeGroupCollection = GetPropertyTypeGroupCollection(dto, memberType, standardPropertyTypes); memberType.PropertyGroups = propertyTypeGroupCollection; - var propertyTypes = GetPropertyTypes(dto, memberType); + var propertyTypes = GetPropertyTypes(dto, memberType, standardPropertyTypes); - //By Convention we add 9 stnd PropertyTypes - This is only here to support loading of types that didn't have these conventions before. - var standardPropertyTypes = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); + //By Convention we add 9 stnd PropertyTypes - This is only here to support loading of types that didn't have these conventions before. foreach (var standardPropertyType in standardPropertyTypes) { if(dto.PropertyTypes.Any(x => x.Alias.Equals(standardPropertyType.Key))) continue; @@ -55,10 +56,10 @@ namespace Umbraco.Core.Persistence.Factories return memberType; } - private PropertyGroupCollection GetPropertyTypeGroupCollection(MemberTypeReadOnlyDto dto, MemberType memberType) + private PropertyGroupCollection GetPropertyTypeGroupCollection(MemberTypeReadOnlyDto dto, MemberType memberType, Dictionary standardProps) { - var propertyGroups = new PropertyGroupCollection(); - var standardProps = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); + var propertyGroups = new PropertyGroupCollection(); + foreach (var groupDto in dto.PropertyTypeGroups.Where(x => x.Id.HasValue)) { var group = new PropertyGroup(); @@ -91,13 +92,19 @@ namespace Umbraco.Core.Persistence.Factories var tempGroupDto = groupDto; - var propertyType = new PropertyType(typeDto.ControlId, - //ensures that any built-in membership properties have their correct dbtype assigned no matter - //what the underlying data type is - MemberTypeRepository.GetDbTypeForBuiltInProperty( - typeDto.Alias, - typeDto.DbType.EnumParse(true), - standardProps).Result) + //ensures that any built-in membership properties have their correct dbtype assigned no matter + //what the underlying data type is + var propDbType = MemberTypeRepository.GetDbTypeForBuiltInProperty( + typeDto.Alias, + typeDto.DbType.EnumParse(true), + standardProps); + + var propertyType = new PropertyType( + typeDto.ControlId, + propDbType.Result, + //This flag tells the property type that it has an explicit dbtype and that it cannot be changed + // which is what we want for the built-in properties. + propDbType.Success) { Alias = typeDto.Alias, DataTypeDefinitionId = typeDto.DataTypeId, @@ -126,34 +133,47 @@ namespace Umbraco.Core.Persistence.Factories return propertyGroups; } - - private List GetPropertyTypes(MemberTypeReadOnlyDto dto, MemberType memberType) + + private List GetPropertyTypes(MemberTypeReadOnlyDto dto, MemberType memberType, Dictionary standardProps) { //Find PropertyTypes that does not belong to a PropertyTypeGroup var propertyTypes = new List(); - foreach (var propertyType in dto.PropertyTypes.Where(x => (x.PropertyTypeGroupId.HasValue == false || x.PropertyTypeGroupId.Value == 0) && x.Id.HasValue)) + foreach (var typeDto in dto.PropertyTypes.Where(x => (x.PropertyTypeGroupId.HasValue == false || x.PropertyTypeGroupId.Value == 0) && x.Id.HasValue)) { //Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType - memberType.MemberTypePropertyTypes.Add(propertyType.Alias, - new MemberTypePropertyProfileAccess(propertyType.ViewOnProfile, propertyType.CanEdit)); - //PropertyType Collection - propertyTypes.Add(new PropertyType(propertyType.ControlId, - propertyType.DbType.EnumParse(true)) - { - Alias = propertyType.Alias, - DataTypeDefinitionId = propertyType.DataTypeId, - Description = propertyType.Description, - HelpText = propertyType.HelpText, - Id = propertyType.Id.Value, - Mandatory = propertyType.Mandatory, - Name = propertyType.Name, - SortOrder = propertyType.SortOrder, - ValidationRegExp = propertyType.ValidationRegExp, - PropertyGroupId = new Lazy(() => default(int)), - CreateDate = dto.CreateDate, - UpdateDate = dto.CreateDate - }); + memberType.MemberTypePropertyTypes.Add(typeDto.Alias, + new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit)); + + //ensures that any built-in membership properties have their correct dbtype assigned no matter + //what the underlying data type is + var propDbType = MemberTypeRepository.GetDbTypeForBuiltInProperty( + typeDto.Alias, + typeDto.DbType.EnumParse(true), + standardProps); + + var propertyType = new PropertyType( + typeDto.ControlId, + propDbType.Result, + //This flag tells the property type that it has an explicit dbtype and that it cannot be changed + // which is what we want for the built-in properties. + propDbType.Success) + { + Alias = typeDto.Alias, + DataTypeDefinitionId = typeDto.DataTypeId, + Description = typeDto.Description, + HelpText = typeDto.HelpText, + Id = typeDto.Id.Value, + Mandatory = typeDto.Mandatory, + Name = typeDto.Name, + SortOrder = typeDto.SortOrder, + ValidationRegExp = typeDto.ValidationRegExp, + PropertyGroupId = new Lazy(() => default(int)), + CreateDate = dto.CreateDate, + UpdateDate = dto.CreateDate + }; + + propertyTypes.Add(propertyType); } return propertyTypes; } diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs index 57cc79bc32..b3382df1e3 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs @@ -11,17 +11,21 @@ namespace Umbraco.Core.Persistence.Factories private readonly int _id; private readonly DateTime _createDate; private readonly DateTime _updateDate; + //a callback to create a property type which can be injected via a contructor + private readonly Func _propertyTypeCtor; public PropertyGroupFactory(int id) { _id = id; + _propertyTypeCtor = (guid, dbType, alias) => new PropertyType(guid, dbType); } - - public PropertyGroupFactory(int id, DateTime createDate, DateTime updateDate) + + public PropertyGroupFactory(int id, DateTime createDate, DateTime updateDate, Func propertyTypeCtor) { _id = id; _createDate = createDate; _updateDate = updateDate; + _propertyTypeCtor = propertyTypeCtor; } #region Implementation of IEntityFactory,IEnumerable> @@ -55,22 +59,23 @@ namespace Umbraco.Core.Persistence.Factories foreach (var typeDto in typeDtos) { var tempGroupDto = groupDto; - var propertyType = new PropertyType(typeDto.DataTypeDto.ControlId, - typeDto.DataTypeDto.DbType.EnumParse(true)) - { - Alias = typeDto.Alias, - DataTypeDefinitionId = typeDto.DataTypeId, - Description = typeDto.Description, - Id = typeDto.Id, - Name = typeDto.Name, - HelpText = typeDto.HelpText, - Mandatory = typeDto.Mandatory, - SortOrder = typeDto.SortOrder, - ValidationRegExp = typeDto.ValidationRegExp, - PropertyGroupId = new Lazy(() => tempGroupDto.Id), - CreateDate = _createDate, - UpdateDate = _updateDate - }; + var propertyType = _propertyTypeCtor(typeDto.DataTypeDto.ControlId, + typeDto.DataTypeDto.DbType.EnumParse(true), + typeDto.Alias); + + propertyType.Alias = typeDto.Alias; + propertyType.DataTypeDefinitionId = typeDto.DataTypeId; + propertyType.Description = typeDto.Description; + propertyType.Id = typeDto.Id; + propertyType.Name = typeDto.Name; + propertyType.HelpText = typeDto.HelpText; + propertyType.Mandatory = typeDto.Mandatory; + propertyType.SortOrder = typeDto.SortOrder; + propertyType.ValidationRegExp = typeDto.ValidationRegExp; + propertyType.PropertyGroupId = new Lazy(() => tempGroupDto.Id); + propertyType.CreateDate = _createDate; + propertyType.UpdateDate = _updateDate; + //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 propertyType.ResetDirtyProperties(false); diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index 4d0e925461..5b761ad559 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -56,6 +56,11 @@ namespace Umbraco.Core.Persistence.Repositories } } + protected virtual PropertyType CreatePropertyType(Guid dataTypeId, DataTypeDatabaseType dbType, string propertyTypeAlias) + { + return new PropertyType(dataTypeId, dbType); + } + protected void PersistNewBaseContentType(ContentTypeDto dto, IContentTypeComposition entity) { //Logic for setting Path, Level and SortOrder @@ -344,7 +349,7 @@ namespace Umbraco.Core.Persistence.Repositories var dtos = Database.Fetch(new GroupPropertyTypeRelator().Map, sql); - var propertyGroupFactory = new PropertyGroupFactory(id, createDate, updateDate); + var propertyGroupFactory = new PropertyGroupFactory(id, createDate, updateDate, CreatePropertyType); var propertyGroups = propertyGroupFactory.BuildEntity(dtos); return new PropertyGroupCollection(propertyGroups); } @@ -361,25 +366,23 @@ namespace Umbraco.Core.Persistence.Repositories var dtos = Database.Fetch(sql); //TODO Move this to a PropertyTypeFactory - var list = (from dto in dtos - where (dto.PropertyTypeGroupId > 0) == false - select - new PropertyType(dto.DataTypeDto.ControlId, - dto.DataTypeDto.DbType.EnumParse(true)) - { - Alias = dto.Alias, - DataTypeDefinitionId = dto.DataTypeId, - Description = dto.Description, - Id = dto.Id, - Name = dto.Name, - HelpText = dto.HelpText, - Mandatory = dto.Mandatory, - SortOrder = dto.SortOrder, - ValidationRegExp = dto.ValidationRegExp, - CreateDate = createDate, - UpdateDate = updateDate - }).ToList(); - + var list = new List(); + foreach (var dto in dtos.Where(x => (x.PropertyTypeGroupId > 0) == false)) + { + var propType = CreatePropertyType(dto.DataTypeDto.ControlId, dto.DataTypeDto.DbType.EnumParse(true), dto.Alias); + propType.Alias = dto.Alias; + propType.DataTypeDefinitionId = dto.DataTypeId; + propType.Description = dto.Description; + propType.Id = dto.Id; + propType.Name = dto.Name; + propType.HelpText = dto.HelpText; + propType.Mandatory = dto.Mandatory; + propType.SortOrder = dto.SortOrder; + propType.ValidationRegExp = dto.ValidationRegExp; + propType.CreateDate = createDate; + propType.UpdateDate = updateDate; + list.Add(propType); + } //Reset dirty properties Parallel.ForEach(list, currentFile => currentFile.ResetDirtyProperties(false)); diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs index 0fa8811d26..9875943f33 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs @@ -18,17 +18,17 @@ namespace Umbraco.Core.Persistence.Repositories /// internal class MemberTypeRepository : ContentTypeBaseRepository, IMemberTypeRepository { - public MemberTypeRepository(IDatabaseUnitOfWork work) + public MemberTypeRepository(IDatabaseUnitOfWork work) : base(work) { } - public MemberTypeRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache) + public MemberTypeRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache) : base(work, cache) { } - #region Overrides of RepositoryBase + #region Overrides of RepositoryBase protected override IMemberType PerformGet(int id) { @@ -40,13 +40,13 @@ namespace Umbraco.Core.Persistence.Repositories Database.Fetch( new PropertyTypePropertyGroupRelator().Map, sql); - if (dtos == null || dtos.Any() == false) - return null; + if (dtos == null || dtos.Any() == false) + return null; - var factory = new MemberTypeReadOnlyFactory(); - var member = factory.BuildEntity(dtos.First()); + var factory = new MemberTypeReadOnlyFactory(); + var member = factory.BuildEntity(dtos.First()); - return member; + return member; } protected override IEnumerable PerformGetAll(params int[] ids) @@ -99,11 +99,11 @@ namespace Umbraco.Core.Persistence.Repositories return sql; } - sql.Select("umbracoNode.*", "cmsContentType.*", "cmsPropertyType.id AS PropertyTypeId", "cmsPropertyType.Alias", + sql.Select("umbracoNode.*", "cmsContentType.*", "cmsPropertyType.id AS PropertyTypeId", "cmsPropertyType.Alias", "cmsPropertyType.Name", "cmsPropertyType.Description", "cmsPropertyType.helpText", "cmsPropertyType.mandatory", "cmsPropertyType.validationRegExp", "cmsPropertyType.dataTypeId", "cmsPropertyType.sortOrder AS PropertyTypeSortOrder", "cmsPropertyType.propertyTypeGroupId AS PropertyTypesGroupId", "cmsMemberType.memberCanEdit", "cmsMemberType.viewOnProfile", - "cmsDataType.controlId", "cmsDataType.dbType", "cmsPropertyTypeGroup.id AS PropertyTypeGroupId", + "cmsDataType.controlId", "cmsDataType.dbType", "cmsPropertyTypeGroup.id AS PropertyTypeGroupId", "cmsPropertyTypeGroup.text AS PropertyGroupName", "cmsPropertyTypeGroup.parentGroupId", "cmsPropertyTypeGroup.sortorder AS PropertyGroupSortOrder", "cmsPropertyTypeGroup.contenttypeNodeId") .From() @@ -179,10 +179,10 @@ namespace Umbraco.Core.Persistence.Repositories var factory = new MemberTypeFactory(NodeObjectTypeId); var dto = factory.BuildDto(entity); - EnsureCorrectDbTypeForBuiltInProperties(entity); + EnsureExplicitDataTypeForBuiltInProperties(entity); PersistNewBaseContentType(dto, entity); - + //Handles the MemberTypeDto (cmsMemberType table) var memberTypeDtos = factory.BuildMemberTypeDtos(entity); foreach (var memberTypeDto in memberTypeDtos) @@ -216,12 +216,12 @@ namespace Umbraco.Core.Persistence.Repositories var factory = new MemberTypeFactory(NodeObjectTypeId); var dto = factory.BuildDto(entity); - EnsureCorrectDbTypeForBuiltInProperties(entity); + EnsureExplicitDataTypeForBuiltInProperties(entity); PersistUpdatedBaseContentType(dto, entity); //Remove existing entries before inserting new ones - Database.Delete("WHERE NodeId = @Id", new {Id = entity.Id}); + Database.Delete("WHERE NodeId = @Id", new { Id = entity.Id }); //Handles the MemberTypeDto (cmsMemberType table) var memberTypeDtos = factory.BuildMemberTypeDtos(entity); @@ -236,11 +236,29 @@ namespace Umbraco.Core.Persistence.Repositories #endregion /// - /// Ensure that all the built-in membership provider properties have their correct db types - /// and property editors assigned. + /// Override so we can specify explicit db type's on any property types that are built-in. + /// + /// + /// + /// + /// + protected override PropertyType CreatePropertyType(Guid dataTypeId, DataTypeDatabaseType dbType, string propertyTypeAlias) + { + //custom property type constructor logic to set explicit dbtype's for built in properties + var stdProps = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); + var propDbType = GetDbTypeForBuiltInProperty(propertyTypeAlias, dbType, stdProps); + return new PropertyType(dataTypeId, propDbType.Result, + //This flag tells the property type that it has an explicit dbtype and that it cannot be changed + // which is what we want for the built-in properties. + propDbType.Success); + } + + /// + /// Ensure that all the built-in membership provider properties have their correct data type + /// and property editors assigned. This occurs prior to saving so that the correct values are persisted. /// /// - private static void EnsureCorrectDbTypeForBuiltInProperties(IContentTypeBase memberType) + private static void EnsureExplicitDataTypeForBuiltInProperties(IContentTypeBase memberType) { var stdProps = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); foreach (var propertyType in memberType.PropertyTypes) @@ -248,7 +266,6 @@ namespace Umbraco.Core.Persistence.Repositories var dbTypeAttempt = GetDbTypeForBuiltInProperty(propertyType.Alias, propertyType.DataTypeDatabaseType, stdProps); if (dbTypeAttempt) { - propertyType.DataTypeDatabaseType = dbTypeAttempt.Result; //this reset's it's current data type reference which will be re-assigned based on the property editor assigned on the next line propertyType.DataTypeDefinitionId = 0; propertyType.DataTypeId = GetPropertyEditorForBuiltInProperty(propertyType.Alias, propertyType.DataTypeId, stdProps).Result; @@ -281,7 +298,7 @@ namespace Umbraco.Core.Persistence.Repositories /// Successful attempt if it was a built in property /// internal static Attempt GetDbTypeForBuiltInProperty( - string propAlias, + string propAlias, DataTypeDatabaseType dbType, Dictionary standardProps) { @@ -307,7 +324,7 @@ namespace Umbraco.Core.Persistence.Repositories /// Successful attempt if it was a built in property /// internal static Attempt GetPropertyEditorForBuiltInProperty( - string propAlias, + string propAlias, Guid propertyEditor, Dictionary standardProps) { diff --git a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs index fd96dc7a54..f0a8378fc9 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs @@ -208,7 +208,9 @@ namespace Umbraco.Core.Persistence.Repositories UserId = p.Item1 }).ToArray(); - _unitOfWork.Database.BulkInsertRecords(actions); + _unitOfWork.Database.BulkInsertRecords(actions, trans); + + trans.Complete(); //Raise the event AssignedPermissions.RaiseEvent( diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs index 52dbbb600f..63e9956f11 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs @@ -61,7 +61,7 @@ namespace Umbraco.Tests.Persistence.Repositories var standardProps = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); Assert.That(sut, Is.Not.Null); - Assert.That(sut.PropertyGroups.Count(), Is.EqualTo(1)); + Assert.That(sut.PropertyGroups.Count(), Is.EqualTo(2)); Assert.That(sut.PropertyTypes.Count(), Is.EqualTo(3 + standardProps.Count)); Assert.That(sut.PropertyGroups.Any(x => x.HasIdentity == false || x.Id == 0), Is.False); @@ -149,7 +149,7 @@ namespace Umbraco.Tests.Persistence.Repositories memberType = repository.Get(memberType.Id); Assert.That(memberType.PropertyTypes.Count(), Is.EqualTo(3 + Constants.Conventions.Member.GetStandardPropertyTypeStubs().Count)); - Assert.That(memberType.PropertyGroups.Count(), Is.EqualTo(1)); + Assert.That(memberType.PropertyGroups.Count(), Is.EqualTo(2)); } } diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index 4b13acf7ee..794c9b0401 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -79,7 +79,8 @@ namespace Umbraco.Tests.Routing var docRequest = new PublishedContentRequest(routingContext.UmbracoContext.CleanedUmbracoUrl, routingContext) { PublishedContent = routingContext.UmbracoContext.ContentCache.GetById(1174), - TemplateModel = template + TemplateModel = template, + RenderingEngine = RenderingEngine.Mvc }; var handler = new RenderRouteHandler(new TestControllerFactory(), routingContext.UmbracoContext); diff --git a/src/Umbraco.Tests/Services/MemberServiceTests.cs b/src/Umbraco.Tests/Services/MemberServiceTests.cs index f67dd55abe..380f050224 100644 --- a/src/Umbraco.Tests/Services/MemberServiceTests.cs +++ b/src/Umbraco.Tests/Services/MemberServiceTests.cs @@ -5,6 +5,7 @@ using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers.Entities; @@ -924,5 +925,41 @@ namespace Umbraco.Tests.Services Assert.IsTrue(found.Comments.IsNullOrWhiteSpace()); } + /// + /// Because we are forcing some of the built-ins to be Labels which have an underlying db type as nvarchar but we need + /// to ensure that the dates/int get saved to the correct column anyways. + /// + [Test] + public void Setting_DateTime_Property_On_Built_In_Member_Property_Saves_To_Correct_Column() + { + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + ServiceContext.MemberTypeService.Save(memberType); + var member = MockedMember.CreateSimpleMember(memberType, "test", "test@test.com", "test", "test"); + var date = DateTime.Now; + member.LastLoginDate = DateTime.Now; + ServiceContext.MemberService.Save(member); + + var result = ServiceContext.MemberService.GetById(member.Id); + Assert.AreEqual( + date.TruncateTo(DateTimeExtensions.DateTruncate.Second), + result.LastLoginDate.TruncateTo(DateTimeExtensions.DateTruncate.Second)); + + //now ensure the col is correct + var sql = new Sql().Select("cmsPropertyData.*") + .From() + .InnerJoin() + .On(dto => dto.PropertyTypeId, dto => dto.Id) + .Where(dto => dto.NodeId == member.Id) + .Where(dto => dto.Alias == Constants.Conventions.Member.LastLoginDate); + + var colResult = DatabaseContext.Database.Fetch(sql); + + Assert.AreEqual(1, colResult.Count); + Assert.IsTrue(colResult.First().Date.HasValue); + Assert.IsFalse(colResult.First().Integer.HasValue); + Assert.IsNull(colResult.First().Text); + Assert.IsNull(colResult.First().VarChar); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs index 899661ead6..78f7ab09a8 100644 --- a/src/Umbraco.Web/Security/MembershipHelper.cs +++ b/src/Umbraco.Web/Security/MembershipHelper.cs @@ -227,7 +227,10 @@ namespace Umbraco.Web.Security var memberType = member.ContentType; - foreach (var prop in memberType.PropertyTypes.Where(x => memberType.MemberCanEditProperty(x.Alias))) + var builtIns = Constants.Conventions.Member.GetStandardPropertyTypeStubs().Select(x => x.Key).ToArray(); + + foreach (var prop in memberType.PropertyTypes + .Where(x => builtIns.Contains(x.Alias) == false && memberType.MemberCanEditProperty(x.Alias))) { var value = string.Empty; var propValue = member.Properties[prop.Alias];