diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index df2e15284d..e91019dca2 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -100,45 +100,63 @@ /// public const string PasswordQuestion = "umbracoPasswordRetrievalQuestionPropertyTypeAlias"; + public const string PasswordQuestionLabel = "Password Question"; + /// /// Property alias for Members Password Answer /// public const string PasswordAnswer = "umbracoPasswordRetrievalAnswerPropertyTypeAlias"; + public const string PasswordAnswerLabel = "Password Answer"; + /// /// Property alias for the Comments on a Member /// public const string Comments = "umbracoCommentPropertyTypeAlias"; + public const string CommentsLabel = "Comments"; + /// /// Property alias for the Approved boolean of a Member /// public const string IsApproved = "umbracoApprovePropertyTypeAlias"; + public const string IsApprovedLabel = "Is Approved"; + /// /// Property alias for the Locked out boolean of a Member /// public const string IsLockedOut = "umbracoLockPropertyTypeAlias"; + public const string IsLockedOutLabel = "Is Locked Out"; + /// /// Property alias for the last date the Member logged in /// public const string LastLoginDate = "umbracoLastLoginPropertyTypeAlias"; + public const string LastLoginDateLabel = "Last Login Date"; + /// /// Property alias for the last date a Member changed its password /// public const string LastPasswordChangeDate = "umbracoMemberLastPasswordChange"; + public const string LastPasswordChangeDateLabel = "Last Password Change Date"; + /// /// Property alias for the last date a Member was locked out /// public const string LastLockoutDate = "umbracoMemberLastLockout"; + public const string LastLockoutDateLabel = "Last Lockout Date"; + /// /// Property alias for the number of failed login attemps /// public const string FailedPasswordAttempts = "umbracoFailedPasswordAttemptsPropertyTypeAlias"; + + public const string FailedPasswordAttemptsLabel = "Failed Password Attempts"; } /// diff --git a/src/Umbraco.Core/Models/Membership/Member.cs b/src/Umbraco.Core/Models/Membership/Member.cs index 5c641a9be2..8fb688462a 100644 --- a/src/Umbraco.Core/Models/Membership/Member.cs +++ b/src/Umbraco.Core/Models/Membership/Member.cs @@ -42,7 +42,7 @@ namespace Umbraco.Core.Models.Membership /// Integer Id /// [DataMember] - public new int Id + public override int Id { get { @@ -65,7 +65,7 @@ namespace Umbraco.Core.Models.Membership /// The key is currectly used to store the Unique Id from the /// umbracoNode table, which many of the entities are based on. [DataMember] - public Guid Key + public override Guid Key { get { @@ -88,7 +88,7 @@ namespace Umbraco.Core.Models.Membership /// Gets or sets the Created Date /// [DataMember] - public DateTime CreateDate + public override DateTime CreateDate { get { return _createDate; } set @@ -105,7 +105,7 @@ namespace Umbraco.Core.Models.Membership /// Gets or sets the Modified Date /// [DataMember] - public DateTime UpdateDate + public override DateTime UpdateDate { get { return _updateDate; } set @@ -122,7 +122,7 @@ namespace Umbraco.Core.Models.Membership /// Indicates whether the current entity has an identity, eg. Id. /// [IgnoreDataMember] - public virtual bool HasIdentity + public override bool HasIdentity { get { @@ -168,7 +168,9 @@ namespace Umbraco.Core.Models.Membership { get { - return Properties[Constants.Conventions.Member.PasswordQuestion].Value.ToString(); + return Properties[Constants.Conventions.Member.PasswordQuestion].Value == null + ? string.Empty + : Properties[Constants.Conventions.Member.PasswordQuestion].Value.ToString(); } set { @@ -188,7 +190,9 @@ namespace Umbraco.Core.Models.Membership { get { - return Properties[Constants.Conventions.Member.PasswordAnswer].Value.ToString(); + return Properties[Constants.Conventions.Member.PasswordAnswer].Value == null + ? string.Empty + : Properties[Constants.Conventions.Member.PasswordAnswer].Value.ToString(); } set { @@ -208,7 +212,9 @@ namespace Umbraco.Core.Models.Membership { get { - return Properties[Constants.Conventions.Member.Comments].Value.ToString(); + return Properties[Constants.Conventions.Member.Comments].Value == null + ? string.Empty + : Properties[Constants.Conventions.Member.Comments].Value.ToString(); } set { @@ -228,6 +234,9 @@ namespace Umbraco.Core.Models.Membership { get { + if (Properties[Constants.Conventions.Member.IsApproved].Value == null) + return default(bool); + if (Properties[Constants.Conventions.Member.IsApproved].Value is bool) return (bool)Properties[Constants.Conventions.Member.IsApproved].Value; @@ -251,6 +260,9 @@ namespace Umbraco.Core.Models.Membership { get { + if (Properties[Constants.Conventions.Member.IsLockedOut].Value == null) + return default(bool); + if (Properties[Constants.Conventions.Member.IsLockedOut].Value is bool) return (bool)Properties[Constants.Conventions.Member.IsLockedOut].Value; @@ -274,6 +286,9 @@ namespace Umbraco.Core.Models.Membership { get { + if (Properties[Constants.Conventions.Member.LastLoginDate].Value == null) + return default(DateTime); + if (Properties[Constants.Conventions.Member.LastLoginDate].Value is DateTime) return (DateTime)Properties[Constants.Conventions.Member.LastLoginDate].Value; @@ -297,6 +312,9 @@ namespace Umbraco.Core.Models.Membership { get { + if (Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value == null) + return default(DateTime); + if (Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value is DateTime) return (DateTime)Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value; @@ -320,6 +338,9 @@ namespace Umbraco.Core.Models.Membership { get { + if (Properties[Constants.Conventions.Member.LastLockoutDate].Value == null) + return default(DateTime); + if (Properties[Constants.Conventions.Member.LastLockoutDate].Value is DateTime) return (DateTime)Properties[Constants.Conventions.Member.LastLockoutDate].Value; @@ -344,6 +365,9 @@ namespace Umbraco.Core.Models.Membership { get { + if (Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value == null) + return default(int); + if (Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value is int) return (int)Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value; diff --git a/src/Umbraco.Core/Models/Membership/MemberProfile.cs b/src/Umbraco.Core/Models/Membership/MemberProfile.cs index aef6af3fd5..6bf7eb33d3 100644 --- a/src/Umbraco.Core/Models/Membership/MemberProfile.cs +++ b/src/Umbraco.Core/Models/Membership/MemberProfile.cs @@ -1,7 +1,133 @@ -namespace Umbraco.Core.Models.Membership +using System; +using System.Reflection; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models.Membership { - internal class MemberProfile : Profile + internal abstract class MemberProfile : Profile, IUmbracoEntity { - + private Lazy _parentId; + private int _sortOrder; + private int _level; + private string _path; + private int _creatorId; + private bool _trashed; + + private static readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo(x => ((IUmbracoEntity)x).ParentId); + private static readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo(x => ((IUmbracoEntity)x).SortOrder); + private static readonly PropertyInfo LevelSelector = ExpressionHelper.GetPropertyInfo(x => ((IUmbracoEntity)x).Level); + private static readonly PropertyInfo PathSelector = ExpressionHelper.GetPropertyInfo(x => ((IUmbracoEntity)x).Path); + private static readonly PropertyInfo CreatorIdSelector = ExpressionHelper.GetPropertyInfo(x => ((IUmbracoEntity)x).CreatorId); + private static readonly PropertyInfo TrashedSelector = ExpressionHelper.GetPropertyInfo(x => x.Trashed); + + public abstract new int Id { get; set; } + public abstract Guid Key { get; set; } + public abstract DateTime CreateDate { get; set; } + public abstract DateTime UpdateDate { get; set; } + public abstract bool HasIdentity { get; protected set; } + + /// + /// Profile of the user who created this Content + /// + int IUmbracoEntity.CreatorId + { + get + { + return _creatorId; + } + set + { + SetPropertyValueAndDetectChanges(o => + { + _creatorId = value; + return _creatorId; + }, _creatorId, CreatorIdSelector); + } + } + + /// + /// Gets or sets the level of the content entity + /// + int IUmbracoEntity.Level + { + get { return _level; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _level = value; + return _level; + }, _level, LevelSelector); + } } + + /// + /// Gets or sets the Id of the Parent entity + /// + int IUmbracoEntity.ParentId + { + get + { + var val = _parentId.Value; + if (val == 0) + { + throw new InvalidOperationException("The ParentId cannot have a value of 0. Perhaps the parent object used to instantiate this object has not been persisted to the data store."); + } + return val; + } + set + { + _parentId = new Lazy(() => value); + OnPropertyChanged(ParentIdSelector); + } + } + + /// + /// Gets or sets the path + /// + string IUmbracoEntity.Path + { + get { return _path; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _path = value; + return _path; + }, _path, PathSelector); + } + } + + /// + /// Gets or sets the sort order of the content entity + /// + int IUmbracoEntity.SortOrder + { + get { return _sortOrder; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _sortOrder = value; + return _sortOrder; + }, _sortOrder, SortOrderSelector); + } + } + + /// + /// Boolean indicating whether this Content is Trashed or not. + /// If Content is Trashed it will be located in the Recyclebin. + /// + public virtual bool Trashed + { + get { return _trashed; } + internal set + { + SetPropertyValueAndDetectChanges(o => + { + _trashed = value; + return _trashed; + }, _trashed, TrashedSelector); + } + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyDataReadOnlyDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyDataReadOnlyDto.cs index 138a66f3eb..f684d104ac 100644 --- a/src/Umbraco.Core/Models/Rdbms/PropertyDataReadOnlyDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/PropertyDataReadOnlyDto.cs @@ -3,34 +3,15 @@ using Umbraco.Core.Persistence; namespace Umbraco.Core.Models.Rdbms { - [TableName("cmsPropertyData")] + [TableName("cmsPropertyType")] [PrimaryKey("id")] [ExplicitColumns] public class PropertyDataReadOnlyDto { - /* cmsPropertyData */ + /* cmsPropertyType */ [Column("id")] public int Id { get; set; } - [Column("VersionId")] - public Guid? VersionId { get; set; } - - [Column("propertytypeid")] - public int PropertyTypeId { get; set; } - - [Column("dataInt")] - public int? Integer { get; set; } - - [Column("dataDate")] - public DateTime? Date { get; set; } - - [Column("dataNvarchar")] - public string VarChar { get; set; } - - [Column("dataNtext")] - public string Text { get; set; } - - /* cmsPropertyType */ [Column("dataTypeId")] public int DataTypeId { get; set; } @@ -55,6 +36,9 @@ namespace Umbraco.Core.Models.Rdbms [Column("Description")] public string Description { get; set; } + [Column("PropertyTypeSortOrder")] + public int SortOrder { get; set; } + /* cmsDataType */ [Column("controlId")] public Guid ControlId { get; set; } @@ -62,6 +46,28 @@ namespace Umbraco.Core.Models.Rdbms [Column("dbType")] public string DbType { get; set; } + /* cmsPropertyData */ + [Column("PropertyDataId")] + public int? PropertyDataId { get; set; } + + [Column("propertytypeid")] + public int? PropertyTypeId { get; set; } + + [Column("VersionId")] + public Guid VersionId { get; set; } + + [Column("dataInt")] + public int? Integer { get; set; } + + [Column("dataDate")] + public DateTime? Date { get; set; } + + [Column("dataNvarchar")] + public string VarChar { get; set; } + + [Column("dataNtext")] + public string Text { get; set; } + [Ignore] public object GetValue { @@ -87,7 +93,7 @@ namespace Umbraco.Core.Models.Rdbms return Text; } - return null; + return string.Empty; } } } diff --git a/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs index 021f67d9f0..67c6a29e37 100644 --- a/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs @@ -1,6 +1,8 @@ using System; +using System.Linq; using System.Collections.Generic; using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; @@ -19,44 +21,174 @@ namespace Umbraco.Core.Persistence.Factories Email = dto.Email, Username = dto.LoginName, Password = dto.Password, - ProviderUserKey = dto.UniqueId + ProviderUserKey = dto.UniqueId, + Trashed = dto.Trashed, + Key = dto.UniqueId.Value, + ProfileId = dto.UniqueId.Value }; - //Create PropertyType - //Use PropertyType to create Property - var properties = new List(); - foreach (var property in dto.Properties) + ((IUmbracoEntity)member).CreatorId = dto.UserId.Value; + ((IUmbracoEntity)member).Level = dto.Level; + ((IUmbracoEntity)member).ParentId = dto.ParentId; + ((IUmbracoEntity)member).Path = dto.Path; + ((IUmbracoEntity)member).SortOrder = dto.SortOrder; + + var propertyTypes = GetStandardPropertyTypeStubs(); + var propertiesDictionary = dto.Properties.ToDictionary(x => x.Alias); + foreach (var property in propertiesDictionary) { - var propertyType = new PropertyType(property.ControlId, - property.DbType.EnumParse(true)) - { - Id = property.PropertyTypeId, - Alias = property.Alias, - Name = property.Name, - Description = property.Description, - HelpText = property.HelpText, - Mandatory = property.Mandatory, - ValidationRegExp = property.ValidationRegExp - //SortOrder - //PropertyGroupId - }; - - propertyType.ResetDirtyProperties(false); - var prop = propertyType.CreatePropertyFromRawValue(property.GetValue, property.VersionId.Value, property.Id); - properties.Add(prop); + if (propertyTypes.ContainsKey(property.Key)) + { + UpdatePropertyType(propertyTypes[property.Key], property.Value); + } + else + { + propertyTypes.Add(property.Key, CreateProperty(property.Value)); + } } - + + var properties = CreateProperties(propertyTypes, propertiesDictionary); member.Properties = new PropertyCollection(properties); member.SetProviderUserKeyType(typeof(Guid)); member.ResetDirtyProperties(false); return member; } - public MemberReadOnlyDto BuildDto(IMembershipUser entity) { throw new System.NotImplementedException(); } + + private IEnumerable CreateProperties(Dictionary propertyTypes, Dictionary propertiesDictionary) + { + var properties = new List(); + foreach (var propertyType in propertyTypes) + { + if (propertiesDictionary.ContainsKey(propertyType.Key)) + { + var prop = propertiesDictionary[propertyType.Key]; + if (prop.PropertyDataId.HasValue && prop.PropertyDataId.Value != default(int)) + { + properties.Add(propertyType.Value.CreatePropertyFromRawValue(prop.GetValue, prop.VersionId, prop.PropertyDataId.Value)); + } + else + { + properties.Add(propertyType.Value.CreatePropertyFromValue(prop.GetValue)); + } + } + else + { + properties.Add(propertyType.Value.CreatePropertyFromValue(null)); + } + } + + return properties; + } + + private PropertyType CreateProperty(PropertyDataReadOnlyDto property) + { + var propertyType = new PropertyType(property.ControlId, property.DbType.EnumParse(true)) + { + Id = property.Id, + Alias = property.Alias, + Name = property.Name, + Description = property.Description, + HelpText = property.HelpText, + Mandatory = property.Mandatory, + ValidationRegExp = property.ValidationRegExp, + SortOrder = property.SortOrder + }; + + if(property.PropertyTypeGroupId.HasValue) + propertyType.PropertyGroupId = new Lazy(() => property.PropertyTypeGroupId.Value); + + return propertyType; + } + + private void UpdatePropertyType(PropertyType propertyType, PropertyDataReadOnlyDto property) + { + propertyType.Id = property.Id; + propertyType.Alias = property.Alias; + propertyType.Name = property.Name; + propertyType.Description = property.Description; + propertyType.HelpText = property.HelpText; + propertyType.Mandatory = property.Mandatory; + propertyType.ValidationRegExp = property.ValidationRegExp; + propertyType.SortOrder = property.SortOrder; + + if (property.PropertyTypeGroupId.HasValue) + propertyType.PropertyGroupId = new Lazy(() => property.PropertyTypeGroupId.Value); + } + + private Dictionary GetStandardPropertyTypeStubs() + { + var propertyTypes = new Dictionary(); + + propertyTypes.Add(Constants.Conventions.Member.Comments, + new PropertyType(new Guid(Constants.PropertyEditors.TextboxMultiple), DataTypeDatabaseType.Ntext) + { + Alias = Constants.Conventions.Member.Comments, + Name = Constants.Conventions.Member.CommentsLabel + }); + + propertyTypes.Add(Constants.Conventions.Member.FailedPasswordAttempts, + new PropertyType(new Guid(Constants.PropertyEditors.Integer), DataTypeDatabaseType.Integer) + { + Alias = Constants.Conventions.Member.FailedPasswordAttempts, + Name = Constants.Conventions.Member.FailedPasswordAttemptsLabel + }); + + propertyTypes.Add(Constants.Conventions.Member.IsApproved, + new PropertyType(new Guid(Constants.PropertyEditors.TrueFalse), DataTypeDatabaseType.Integer) + { + Alias = Constants.Conventions.Member.IsApproved, + Name = Constants.Conventions.Member.IsApprovedLabel + }); + + propertyTypes.Add(Constants.Conventions.Member.IsLockedOut, + new PropertyType(new Guid(Constants.PropertyEditors.TrueFalse), DataTypeDatabaseType.Integer) + { + Alias = Constants.Conventions.Member.IsLockedOut, + Name = Constants.Conventions.Member.IsLockedOutLabel + }); + + propertyTypes.Add(Constants.Conventions.Member.LastLockoutDate, + new PropertyType(new Guid(Constants.PropertyEditors.Date), DataTypeDatabaseType.Date) + { + Alias = Constants.Conventions.Member.LastLockoutDate, + Name = Constants.Conventions.Member.LastLockoutDateLabel + }); + + propertyTypes.Add(Constants.Conventions.Member.LastLoginDate, + new PropertyType(new Guid(Constants.PropertyEditors.Date), DataTypeDatabaseType.Date) + { + Alias = Constants.Conventions.Member.LastLoginDate, + Name = Constants.Conventions.Member.LastLoginDateLabel + }); + + propertyTypes.Add(Constants.Conventions.Member.LastPasswordChangeDate, + new PropertyType(new Guid(Constants.PropertyEditors.Date), DataTypeDatabaseType.Date) + { + Alias = Constants.Conventions.Member.LastPasswordChangeDate, + Name = Constants.Conventions.Member.LastPasswordChangeDateLabel + }); + + propertyTypes.Add(Constants.Conventions.Member.PasswordAnswer, + new PropertyType(new Guid(Constants.PropertyEditors.Textbox), DataTypeDatabaseType.Nvarchar) + { + Alias = Constants.Conventions.Member.PasswordAnswer, + Name = Constants.Conventions.Member.PasswordAnswerLabel + }); + + propertyTypes.Add(Constants.Conventions.Member.PasswordQuestion, + new PropertyType(new Guid(Constants.PropertyEditors.Textbox), DataTypeDatabaseType.Nvarchar) + { + Alias = Constants.Conventions.Member.PasswordQuestion, + Name = Constants.Conventions.Member.PasswordQuestionLabel + }); + + return propertyTypes; + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs index fc0bdaac95..cb93a00185 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -4,6 +4,7 @@ using System.Linq; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Caching; +using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Relators; using Umbraco.Core.Persistence.UnitOfWork; @@ -45,7 +46,10 @@ namespace Umbraco.Core.Persistence.Repositories if (dto == null || dto.Any() == false) return null; - return new Member(); + var factory = new MemberReadOnlyFactory(); + var member = factory.BuildEntity(dto.First()); + + return member; } protected override IEnumerable PerformGetAll(params int[] ids) @@ -68,20 +72,21 @@ namespace Umbraco.Core.Persistence.Repositories var sql = new Sql(); sql.Select("umbracoNode.*", "cmsContent.contentType", "cmsContentType.alias AS ContentTypeAlias", "cmsContentVersion.VersionId", "cmsContentVersion.VersionDate", "cmsContentVersion.LanguageLocale", "cmsMember.Email", - "cmsMember.LoginName", "cmsMember.Password", "cmsPropertyData.id", "cmsPropertyData.dataDate", - "cmsPropertyData.dataInt", "cmsPropertyData.dataNtext", "cmsPropertyData.dataNvarchar", - "cmsPropertyData.propertytypeid", "cmsPropertyType.Alias", "cmsPropertyType.Description", + "cmsMember.LoginName", "cmsMember.Password", "cmsPropertyData.id AS PropertyDataId", "cmsPropertyData.propertytypeid", + "cmsPropertyData.dataDate", "cmsPropertyData.dataInt", "cmsPropertyData.dataNtext", "cmsPropertyData.dataNvarchar", + "cmsPropertyType.id", "cmsPropertyType.Alias", "cmsPropertyType.Description", "cmsPropertyType.Name", "cmsPropertyType.mandatory", "cmsPropertyType.validationRegExp", - "cmsPropertyType.helpText", "cmsPropertyType.propertyTypeGroupId", "cmsPropertyType.dataTypeId", - "cmsDataType.controlId", "cmsDataType.dbType") + "cmsPropertyType.helpText", "cmsPropertyType.sortOrder AS PropertyTypeSortOrder", "cmsPropertyType.propertyTypeGroupId", + "cmsPropertyType.dataTypeId", "cmsDataType.controlId", "cmsDataType.dbType") .From() .InnerJoin().On(left => left.NodeId, right => right.NodeId) .InnerJoin().On(left => left.NodeId, right => right.ContentTypeId) .InnerJoin().On(left => left.NodeId, right => right.NodeId) .InnerJoin().On(left => left.NodeId, right => right.NodeId) - .LeftJoin().On(left => left.VersionId, right => right.VersionId) - .LeftJoin().On(left => left.Id, right => right.PropertyTypeId) + .LeftJoin().On(left => left.ContentTypeId, right => right.ContentTypeId) .LeftJoin().On(left => left.DataTypeId, right => right.DataTypeId) + .LeftJoin().On(left => left.PropertyTypeId, right => right.Id) + .Append("AND cmsPropertyData.versionId = cmsContentVersion.VersionId") .Where(x => x.NodeObjectType == NodeObjectTypeId); return sql; }