diff --git a/src/Umbraco.Core/Cache/CacheKeys.cs b/src/Umbraco.Core/Cache/CacheKeys.cs index fc17bd7245..1278ff0e33 100644 --- a/src/Umbraco.Core/Cache/CacheKeys.cs +++ b/src/Umbraco.Core/Cache/CacheKeys.cs @@ -1,3 +1,5 @@ +using System; + namespace Umbraco.Core.Cache { @@ -9,6 +11,7 @@ namespace Umbraco.Core.Cache public const string ApplicationTreeCacheKey = "ApplicationTreeCache"; public const string ApplicationsCacheKey = "ApplicationCache"; + [Obsolete("This is no longer used and will be removed from the codebase in the future")] public const string UserTypeCacheKey = "UserTypeCache"; public const string ContentItemCacheKey = "contentItem"; @@ -24,13 +27,16 @@ namespace Umbraco.Core.Cache public const string MemberLibraryCacheKey = "UL_GetMember"; public const string MemberBusinessLogicCacheKey = "MemberCacheItem_"; - + public const string TemplateFrontEndCacheKey = "template"; public const string TemplateBusinessLogicCacheKey = "UmbracoTemplateCache"; public const string UserContextCacheKey = "UmbracoUserContext"; public const string UserContextTimeoutCacheKey = "UmbracoUserContextTimeout"; + + [Obsolete("This is no longer used and will be removed from the codebase in the future")] public const string UserCacheKey = "UmbracoUser"; + public const string UserPermissionsCacheKey = "UmbracoUserPermissions"; public const string ContentTypeCacheKey = "UmbracoContentType"; diff --git a/src/Umbraco.Core/Configuration/ClientDependencyConfiguration.cs b/src/Umbraco.Core/Configuration/ClientDependencyConfiguration.cs index 0536647f55..9f7ec8083c 100644 --- a/src/Umbraco.Core/Configuration/ClientDependencyConfiguration.cs +++ b/src/Umbraco.Core/Configuration/ClientDependencyConfiguration.cs @@ -28,9 +28,9 @@ namespace Umbraco.Core.Configuration var versionAttribute = clientDependencyConfigXml.Root.Attribute("version"); - int oldVersion; - int.TryParse(versionAttribute.Value, out oldVersion); - var newVersion = oldVersion + 100; + //Set the new version to the hashcode of now + var oldVersion = versionAttribute.Value; + var newVersion = DateTime.UtcNow.GetHashCode(); versionAttribute.SetValue(newVersion); clientDependencyConfigXml.Save(_fileName, SaveOptions.DisableFormatting); diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index d472150a85..dea05d57af 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -287,7 +287,7 @@ namespace Umbraco.Core () => PluginManager.Current.ResolveAssignedMapperTypes()); RepositoryResolver.Current = new RepositoryResolver( - new RepositoryFactory()); + new RepositoryFactory(ApplicationCache)); SqlSyntaxProvidersResolver.Current = new SqlSyntaxProvidersResolver( new[] { typeof(MySqlSyntaxProvider), typeof(SqlCeSyntaxProvider), typeof(SqlServerSyntaxProvider) }) diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index 3fbdeccda8..c18c7c3a43 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -306,10 +306,8 @@ namespace Umbraco.Core.Models /// Value as a public virtual TPassType GetValue(string propertyTypeAlias) { - if (Properties[propertyTypeAlias].Value is TPassType) - return (TPassType)Properties[propertyTypeAlias].Value; - - return (TPassType)Convert.ChangeType(Properties[propertyTypeAlias].Value, typeof(TPassType)); + var convertAttempt = Properties[propertyTypeAlias].Value.TryConvertTo(); + return convertAttempt.Success ? convertAttempt.Result : default(TPassType); } /// diff --git a/src/Umbraco.Core/Models/EntityExtensions.cs b/src/Umbraco.Core/Models/EntityExtensions.cs new file mode 100644 index 0000000000..f461c4007c --- /dev/null +++ b/src/Umbraco.Core/Models/EntityExtensions.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models +{ + public static class EntityExtensions + { + + /// + /// Returns true if this entity has just been created and persisted to the data store + /// + /// + /// + /// + /// This is useful when handling events to determine if an entity is a brand new entity or was + /// already existing. + /// + public static bool IsNewEntity(this IEntity entity) + { + var dirty = (IRememberBeingDirty)entity; + return dirty.WasPropertyDirty("Id"); + } + } +} diff --git a/src/Umbraco.Core/Models/IMemberGroup.cs b/src/Umbraco.Core/Models/IMemberGroup.cs new file mode 100644 index 0000000000..5c3741997b --- /dev/null +++ b/src/Umbraco.Core/Models/IMemberGroup.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models +{ + /// + /// Represents a member type + /// + public interface IMemberGroup : IAggregateRoot + { + /// + /// The name of the member group + /// + string Name { get; set; } + + /// + /// Profile of the user who created this Entity + /// + int CreatorId { get; set; } + + /// + /// Some entities may expose additional data that other's might not, this custom data will be available in this collection + /// + IDictionary AdditionalData { get; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Member.cs b/src/Umbraco.Core/Models/Member.cs index d43a6a0266..6a54bddc70 100644 --- a/src/Umbraco.Core/Models/Member.cs +++ b/src/Umbraco.Core/Models/Member.cs @@ -31,6 +31,7 @@ namespace Umbraco.Core.Models _contentType = contentType; } + //TODO: Should we just get rid of this one? no reason to have a level set. internal Member(string name, string email, string username, string password, int parentId, IMemberType contentType) : base(name, parentId, contentType, new PropertyCollection()) { @@ -210,14 +211,13 @@ namespace Umbraco.Core.Models { 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; - + var tryConvert = Properties[Constants.Conventions.Member.IsApproved].Value.TryConvertTo(); + if (tryConvert.Success) + { + return tryConvert.Result; + } + return default(bool); //TODO: Use TryConvertTo instead - return (bool)Convert.ChangeType(Properties[Constants.Conventions.Member.IsApproved].Value, typeof(bool)); } set { @@ -237,14 +237,13 @@ namespace Umbraco.Core.Models { 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; - + var tryConvert = Properties[Constants.Conventions.Member.IsLockedOut].Value.TryConvertTo(); + if (tryConvert.Success) + { + return tryConvert.Result; + } + return default(bool); //TODO: Use TryConvertTo instead - return (bool)Convert.ChangeType(Properties[Constants.Conventions.Member.IsLockedOut].Value, typeof(bool)); } set { @@ -264,14 +263,13 @@ namespace Umbraco.Core.Models { 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; - + var tryConvert = Properties[Constants.Conventions.Member.LastLoginDate].Value.TryConvertTo(); + if (tryConvert.Success) + { + return tryConvert.Result; + } + return default(DateTime); //TODO: Use TryConvertTo instead - return (DateTime)Convert.ChangeType(Properties[Constants.Conventions.Member.LastLoginDate].Value, typeof(DateTime)); } set { @@ -291,14 +289,13 @@ namespace Umbraco.Core.Models { 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; - + var tryConvert = Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value.TryConvertTo(); + if (tryConvert.Success) + { + return tryConvert.Result; + } + return default(DateTime); //TODO: Use TryConvertTo instead - return (DateTime)Convert.ChangeType(Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value, typeof(DateTime)); } set { @@ -318,14 +315,13 @@ namespace Umbraco.Core.Models { 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; - + var tryConvert = Properties[Constants.Conventions.Member.LastLockoutDate].Value.TryConvertTo(); + if (tryConvert.Success) + { + return tryConvert.Result; + } + return default(DateTime); //TODO: Use TryConvertTo instead - return (DateTime)Convert.ChangeType(Properties[Constants.Conventions.Member.LastLockoutDate].Value, typeof(DateTime)); } set { @@ -346,14 +342,13 @@ namespace Umbraco.Core.Models { 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; - + var tryConvert = Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value.TryConvertTo(); + if (tryConvert.Success) + { + return tryConvert.Result; + } + return default(int); //TODO: Use TryConvertTo instead - return (int)Convert.ChangeType(Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value, typeof(int)); } set { diff --git a/src/Umbraco.Core/Models/MemberGroup.cs b/src/Umbraco.Core/Models/MemberGroup.cs new file mode 100644 index 0000000000..e52448a11d --- /dev/null +++ b/src/Umbraco.Core/Models/MemberGroup.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.Serialization; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models +{ + /// + /// Represents a member type + /// + [Serializable] + [DataContract(IsReference = true)] + public class MemberGroup : Entity, IMemberGroup + { + public MemberGroup() + { + AdditionalData = new Dictionary(); + } + + private string _name; + private int _creatorId; + + private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); + private static readonly PropertyInfo CreatorIdSelector = ExpressionHelper.GetPropertyInfo(x => x.CreatorId); + + [DataMember] + public string Name + { + get { return _name; } + set + { + SetPropertyValueAndDetectChanges(o => + { + if (_name != value) + { + //if the name has changed, add the value to the additional data, + //this is required purely for event handlers to know the previous name of the group + //so we can keep the public access up to date. + AdditionalData["previousName"] = _name; + } + + _name = value; + return _name; + }, _name, NameSelector); + } + } + + public int CreatorId + { + get { return _creatorId; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _creatorId = value; + return _creatorId; + }, _creatorId, CreatorIdSelector); + } + } + + public IDictionary AdditionalData { get; private set; } + + /// + /// Method to call when Entity is being saved + /// + /// Created date is set and a Unique key is assigned + internal override void AddingEntity() + { + base.AddingEntity(); + + if (Key == Guid.Empty) + Key = Guid.NewGuid(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/IProfile.cs b/src/Umbraco.Core/Models/Membership/IProfile.cs index fbbff85e7d..749c3371b8 100644 --- a/src/Umbraco.Core/Models/Membership/IProfile.cs +++ b/src/Umbraco.Core/Models/Membership/IProfile.cs @@ -3,6 +3,10 @@ /// /// Defines the the Profile interface /// + /// + /// This interface is pretty useless but has been exposed publicly from 6.x so we're stuck with it. It would make more sense + /// if the Id was an int but since it's not people have to cast it to int all of the time! + /// public interface IProfile { object Id { get; set; } diff --git a/src/Umbraco.Core/Models/Membership/IUser.cs b/src/Umbraco.Core/Models/Membership/IUser.cs index 3695a84546..46338c8c4a 100644 --- a/src/Umbraco.Core/Models/Membership/IUser.cs +++ b/src/Umbraco.Core/Models/Membership/IUser.cs @@ -7,19 +7,18 @@ namespace Umbraco.Core.Models.Membership /// Defines the interface for a /// /// Will be left internal until a proper Membership implementation is part of the roadmap - internal interface IUser : IMembershipUser, IProfile + public interface IUser : IMembershipUser { - new object Id { get; set; } - + string Name { get; set; } int SessionTimeout { get; set; } int StartContentId { get; set; } int StartMediaId { get; set; } string Language { get; set; } - //NOTE: I have removed this because it is obsolete in v7 and we are basically removing live editing for now - //bool DefaultToLiveEditing { get; set; } - - IUserType UserType { get; } + /// + /// Gets/sets the user type for the user + /// + IUserType UserType { get; set; } /// /// The default permission set for the user @@ -32,5 +31,10 @@ namespace Umbraco.Core.Models.Membership IEnumerable AllowedSections { get; } void RemoveAllowedSection(string sectionAlias); void AddAllowedSection(string sectionAlias); + + /// + /// Exposes the basic profile data + /// + IProfile ProfileData { get; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Membership/IUserType.cs b/src/Umbraco.Core/Models/Membership/IUserType.cs index aa2d882e4c..fe678afd2b 100644 --- a/src/Umbraco.Core/Models/Membership/IUserType.cs +++ b/src/Umbraco.Core/Models/Membership/IUserType.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.Mappers; namespace Umbraco.Core.Models.Membership { - internal interface IUserType : IAggregateRoot + public interface IUserType : IAggregateRoot { /// /// The user type alias diff --git a/src/Umbraco.Core/Models/Membership/User.cs b/src/Umbraco.Core/Models/Membership/User.cs index 26e8bf6cc0..8e55ddf1fb 100644 --- a/src/Umbraco.Core/Models/Membership/User.cs +++ b/src/Umbraco.Core/Models/Membership/User.cs @@ -5,6 +5,7 @@ using System.Collections.Specialized; using System.Linq; using System.Reflection; using System.Runtime.Serialization; +using Umbraco.Core.Configuration; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Persistence.Mappers; @@ -19,7 +20,7 @@ namespace Umbraco.Core.Models.Membership /// [Serializable] [DataContract(IsReference = true)] - internal class User : TracksChangesEntityBase, IUser + public class User : TracksChangesEntityBase, IUser { public User(IUserType userType) { @@ -31,6 +32,7 @@ namespace Umbraco.Core.Models.Membership _sectionCollection = new ObservableCollection(); _addedSections = new List(); _removedSections = new List(); + _language = GlobalSettings.DefaultUILanguage; _sectionCollection.CollectionChanged += SectionCollectionChanged; } @@ -40,12 +42,12 @@ namespace Umbraco.Core.Models.Membership _name = name; _email = email; _username = username; - _password = password; + _password = password; } - private readonly IUserType _userType; + private IUserType _userType; private bool _hasIdentity; - private object _id; + private int _id; private string _name; private Type _userTypeKey; private readonly List _addedSections; @@ -58,7 +60,6 @@ namespace Umbraco.Core.Models.Membership private string _username; private string _email; private string _password; - private Guid _key; private bool _isApproved; private bool _isLockedOut; private string _language; @@ -81,23 +82,43 @@ namespace Umbraco.Core.Models.Membership private static readonly PropertyInfo LanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.Language); private static readonly PropertyInfo DefaultPermissionsSelector = ExpressionHelper.GetPropertyInfo>(x => x.DefaultPermissions); private static readonly PropertyInfo DefaultToLiveEditingSelector = ExpressionHelper.GetPropertyInfo(x => x.DefaultToLiveEditing); + private static readonly PropertyInfo HasIdentitySelector = ExpressionHelper.GetPropertyInfo(x => x.HasIdentity); + private static readonly PropertyInfo UserTypeSelector = ExpressionHelper.GetPropertyInfo(x => x.UserType); #region Implementation of IEntity [IgnoreDataMember] - public bool HasIdentity { get { return Id != null || _hasIdentity; } } - - [IgnoreDataMember] - int IEntity.Id + public bool HasIdentity { get { - return int.Parse(Id.ToString()); + return _hasIdentity; + } + protected set + { + SetPropertyValueAndDetectChanges(o => + { + _hasIdentity = value; + return _hasIdentity; + }, _hasIdentity, HasIdentitySelector); + } + } + + [DataMember] + public int Id + { + get + { + return _id; } set { - Id = value; - _hasIdentity = true; + SetPropertyValueAndDetectChanges(o => + { + _id = value; + HasIdentity = true; //set the has Identity + return _id; + }, _id, IdSelector); } } @@ -239,8 +260,8 @@ namespace Umbraco.Core.Models.Membership public int FailedPasswordAttempts { get; set; } #endregion - - #region Implementation of IProfile + + #region Implementation of IUser [DataMember] public string Name @@ -249,17 +270,13 @@ namespace Umbraco.Core.Models.Membership set { SetPropertyValueAndDetectChanges(o => - { - _name = value; - return _name; - }, _name, NameSelector); + { + _name = value; + return _name; + }, _name, NameSelector); } } - #endregion - - #region Implementation of IUser - public IEnumerable AllowedSections { get { return _sectionCollection; } @@ -278,6 +295,11 @@ namespace Umbraco.Core.Models.Membership } } + public IProfile ProfileData + { + get { return new UserProfile(this); } + } + /// /// Used internally to check if we need to add a section in the repository to the db /// @@ -354,20 +376,6 @@ namespace Umbraco.Core.Models.Membership } } - [DataMember] - public object Id - { - get { return _id; } - set - { - SetPropertyValueAndDetectChanges(o => - { - _id = value; - return _id; - }, _id, IdSelector); - } - } - [DataMember] public string Language { @@ -414,6 +422,19 @@ namespace Umbraco.Core.Models.Membership public IUserType UserType { get { return _userType; } + set + { + if (value.HasIdentity == false) + { + throw new InvalidOperationException("Cannot assign a User Type that has not been persisted"); + } + + SetPropertyValueAndDetectChanges(o => + { + _userType = value; + return _userType; + }, _userType, UserTypeSelector); + } } #endregion @@ -459,5 +480,30 @@ namespace Umbraco.Core.Models.Membership _removedSections.Add(e.OldItems.Cast().First()); } } + + /// + /// Internal class used to wrap the user in a profile + /// + private class UserProfile : IProfile + { + private readonly IUser _user; + + public UserProfile(IUser user) + { + _user = user; + } + + public object Id + { + get { return _user.Id; } + set { _user.Id = (int)value; } + } + + public string Name + { + get { return _user.Name; } + set { _user.Name = value; } + } + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Template.cs b/src/Umbraco.Core/Models/Template.cs index 0b38c19a51..387a345039 100644 --- a/src/Umbraco.Core/Models/Template.cs +++ b/src/Umbraco.Core/Models/Template.cs @@ -187,7 +187,7 @@ namespace Umbraco.Core.Models public void SetMasterTemplate(ITemplate masterTemplate) { - MasterTemplateId = new Lazy(() => {return masterTemplate.Id}); + MasterTemplateId = new Lazy(() => masterTemplate.Id); } } } diff --git a/src/Umbraco.Core/Models/UmbracoEntity.cs b/src/Umbraco.Core/Models/UmbracoEntity.cs index b735f385d8..4be916ce7d 100644 --- a/src/Umbraco.Core/Models/UmbracoEntity.cs +++ b/src/Umbraco.Core/Models/UmbracoEntity.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Reflection; using Umbraco.Core.Models.EntityBase; @@ -53,6 +54,12 @@ namespace Umbraco.Core.Models Trashed = trashed; } + public UmbracoEntity(int trashed) + { + AdditionalData = new Dictionary(); + Trashed = trashed == 1; + } + public int CreatorId { get { return _creatorId; } diff --git a/src/Umbraco.Core/Persistence/Factories/MemberGroupFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberGroupFactory.cs new file mode 100644 index 0000000000..207eea83b3 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Factories/MemberGroupFactory.cs @@ -0,0 +1,64 @@ +using System; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Factories +{ + internal class MemberGroupFactory : IEntityFactory + { + + private readonly Guid _nodeObjectTypeId; + + public MemberGroupFactory() + { + _nodeObjectTypeId = new Guid(Constants.ObjectTypes.MemberGroup); + } + + #region Implementation of IEntityFactory + + public IMemberGroup BuildEntity(NodeDto dto) + { + var template = new MemberGroup + { + CreateDate = dto.CreateDate, + Id = dto.NodeId, + Key = dto.UniqueId.Value, + Name = dto.Text + }; + + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + template.ResetDirtyProperties(false); + return template; + } + + public NodeDto BuildDto(IMemberGroup entity) + { + var dto = new NodeDto + { + CreateDate = entity.CreateDate, + NodeId = entity.Id, + Level = 0, + NodeObjectType = _nodeObjectTypeId, + ParentId = -1, + Path = "", + SortOrder = 0, + Text = entity.Name, + Trashed = false, + UniqueId = entity.Key, + UserId = entity.CreatorId + }; + + if (entity.HasIdentity) + { + dto.NodeId = entity.Id; + dto.Path = "-1," + entity.Id; + } + + return dto; + } + + #endregion + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs index d466b3b5ab..2739ede1af 100644 --- a/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs @@ -25,7 +25,7 @@ namespace Umbraco.Core.Persistence.Factories CreateDate = dto.CreateDate, UpdateDate = dto.UpdateDate, Name = dto.Text, - ProviderUserKey = dto.UniqueId.Value, + ProviderUserKey = dto.NodeId, Trashed = dto.Trashed, Key = dto.UniqueId.Value, CreatorId = dto.UserId.HasValue ? dto.UserId.Value : 0, diff --git a/src/Umbraco.Core/Persistence/Mappers/MemberGroupMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MemberGroupMapper.cs new file mode 100644 index 0000000000..da57738403 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Mappers/MemberGroupMapper.cs @@ -0,0 +1,34 @@ +using System.Collections.Concurrent; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Mappers +{ + [MapperFor(typeof (IMemberGroup))] + [MapperFor(typeof (MemberGroup))] + public sealed class MemberGroupMapper : BaseMapper + { + private static readonly ConcurrentDictionary PropertyInfoCacheInstance = new ConcurrentDictionary(); + + //NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it + // otherwise that would fail because there is no public constructor. + public MemberGroupMapper() + { + BuildMap(); + } + + internal override ConcurrentDictionary PropertyInfoCache + { + get { return PropertyInfoCacheInstance; } + } + + internal override void BuildMap() + { + CacheMap(src => src.Id, dto => dto.NodeId); + CacheMap(src => src.CreateDate, dto => dto.CreateDate); + CacheMap(src => src.CreatorId, dto => dto.UserId); + CacheMap(src => src.Name, dto => dto.Text); + CacheMap(src => src.Key, dto => dto.UniqueId); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs index d0d34478a4..361f1f3949 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs @@ -79,7 +79,10 @@ namespace Umbraco.Core.Persistence.Migrations.Initial try { - _database.DropTable(tableName); + if (_database.TableExist(tableName)) + { + _database.DropTable(tableName); + } } catch (Exception ex) { diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 228b72b411..583af2d25c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -24,8 +24,10 @@ namespace Umbraco.Core.Persistence.Repositories private readonly IContentTypeRepository _contentTypeRepository; private readonly ITemplateRepository _templateRepository; private readonly ITagsRepository _tagRepository; + private readonly CacheHelper _cacheHelper; public ContentRepository(IDatabaseUnitOfWork work, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagsRepository tagRepository) + public ContentRepository(IDatabaseUnitOfWork work, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, CacheHelper cacheHelper) : base(work) { if (contentTypeRepository == null) throw new ArgumentNullException("contentTypeRepository"); @@ -34,11 +36,13 @@ namespace Umbraco.Core.Persistence.Repositories _contentTypeRepository = contentTypeRepository; _templateRepository = templateRepository; _tagRepository = tagRepository; + _cacheHelper = cacheHelper; EnsureUniqueNaming = true; } public ContentRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagsRepository tagRepository) + public ContentRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, CacheHelper cacheHelper) : base(work, cache) { if (contentTypeRepository == null) throw new ArgumentNullException("contentTypeRepository"); @@ -47,6 +51,7 @@ namespace Umbraco.Core.Persistence.Repositories _contentTypeRepository = contentTypeRepository; _templateRepository = templateRepository; _tagRepository = tagRepository; + _cacheHelper = cacheHelper; EnsureUniqueNaming = true; } @@ -274,7 +279,7 @@ namespace Umbraco.Core.Persistence.Repositories //Assign the same permissions to it as the parent node // http://issues.umbraco.org/issue/U4-2161 - var permissionsRepo = new PermissionRepository(UnitOfWork); + var permissionsRepo = new PermissionRepository(UnitOfWork, _cacheHelper); var parentPermissions = permissionsRepo.GetPermissionsForEntity(entity.ParentId).ToArray(); //if there are parent permissions then assign them, otherwise leave null and permissions will become the // user's default permissions. @@ -557,13 +562,13 @@ namespace Umbraco.Core.Persistence.Repositories public void AssignEntityPermissions(IContent entity, char permission, IEnumerable userIds) { - var repo = new PermissionRepository(UnitOfWork); + var repo = new PermissionRepository(UnitOfWork, _cacheHelper); repo.AssignEntityPermissions(entity, permission, userIds); } public IEnumerable GetPermissionsForEntity(int entityId) { - var repo = new PermissionRepository(UnitOfWork); + var repo = new PermissionRepository(UnitOfWork, _cacheHelper); return repo.GetPermissionsForEntity(entityId); } diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index 6afcd8005f..308a4e4937 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -54,21 +54,7 @@ namespace Umbraco.Core.Persistence.Repositories yield return dto.ContentTypeNodeId; } } - - /// - /// We need to override this method to ensure that any content cache is cleared - /// - /// - /// - /// see: http://issues.umbraco.org/issue/U4-1963 - /// - public override void PersistUpdatedItem(IEntity entity) - { - InMemoryCacheProvider.Current.Clear(typeof(IContent)); - RuntimeCacheProvider.Current.Clear(typeof(IContent)); - base.PersistUpdatedItem(entity); - } - + protected void PersistNewBaseContentType(ContentTypeDto dto, IContentTypeComposition entity) { //Logic for setting Path, Level and SortOrder diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberGroupRepository.cs new file mode 100644 index 0000000000..2750457271 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberGroupRepository.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Persistence.Repositories +{ + public interface IMemberGroupRepository : IRepositoryQueryable + { + /// + /// Gets a member group by it's name + /// + /// + /// + IMemberGroup GetByName(string name); + + /// + /// Creates the new member group if it doesn't already exist + /// + /// + IMemberGroup CreateIfNotExists(string roleName); + + /// + /// Returns the member groups for a given member + /// + /// + /// + IEnumerable GetMemberGroupsForMember(int memberId); + + /// + /// Returns the member groups for a given member + /// + /// + /// + IEnumerable GetMemberGroupsForMember(string username); + + void AssignRoles(string[] usernames, string[] roleNames); + + void DissociateRoles(string[] usernames, string[] roleNames); + + void AssignRoles(int[] memberIds, string[] roleNames); + + void DissociateRoles(int[] memberIds, string[] roleNames); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs index 6655652968..ac005aa731 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs @@ -9,6 +9,15 @@ namespace Umbraco.Core.Persistence.Repositories { internal interface IMemberRepository : IRepositoryVersionable { + /// + /// Finds members in a given role + /// + /// + /// + /// + /// + IEnumerable FindMembersInRole(string roleName, string usernameToMatch, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith); + /// /// Get all members in a specific group /// diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs index 1dd753f74d..5587af9dc4 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs @@ -46,7 +46,7 @@ namespace Umbraco.Core.Persistence.Repositories /// /// /// - IEnumerable GetUserPermissionsForEntities(object userId, params int[] entityIds); + IEnumerable GetUserPermissionsForEntities(int userId, params int[] entityIds); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs index a93355ceb3..1d5fca9770 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs @@ -1,7 +1,383 @@ -namespace Umbraco.Core.Persistence.Repositories +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Events; +using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.Caching; +using Umbraco.Core.Persistence.Factories; +using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Services; +using Umbraco.Core.Cache; + +namespace Umbraco.Core.Persistence.Repositories { - public class MemberGroupRepository + + + internal class MemberGroupRepository : PetaPocoRepositoryBase, IMemberGroupRepository { - + private readonly CacheHelper _cacheHelper; + + public MemberGroupRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper) + : base(work) + { + if (cacheHelper == null) throw new ArgumentNullException("cacheHelper"); + _cacheHelper = cacheHelper; + } + + public MemberGroupRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, CacheHelper cacheHelper) + : base(work, cache) + { + if (cacheHelper == null) throw new ArgumentNullException("cacheHelper"); + _cacheHelper = cacheHelper; + } + + private readonly MemberGroupFactory _modelFactory = new MemberGroupFactory(); + + protected override IMemberGroup PerformGet(int id) + { + var sql = GetBaseQuery(false); + sql.Where(GetBaseWhereClause(), new { Id = id }); + + var dto = Database.Fetch(sql).FirstOrDefault(); + + return dto == null ? null : _modelFactory.BuildEntity(dto); + } + + protected override IEnumerable PerformGetAll(params int[] ids) + { + if (ids.Any()) + { + var sql = new Sql() + .Select("*") + .From() + .Where(dto => dto.NodeObjectType == NodeObjectTypeId) + .Where("umbracoNode.id in (@ids)", new { ids = ids }); + return Database.Fetch(sql) + .Select(x => _modelFactory.BuildEntity(x)); + } + else + { + var sql = new Sql() + .From() + .Where(dto => dto.NodeObjectType == NodeObjectTypeId); + return Database.Fetch(sql) + .Select(x => _modelFactory.BuildEntity(x)); + } + } + + protected override IEnumerable PerformGetByQuery(IQuery query) + { + var sqlClause = GetBaseQuery(false); + var translator = new SqlTranslator(sqlClause, query); + var sql = translator.Translate(); + + return Database.Fetch(sql) + .Select(x => _modelFactory.BuildEntity(x)); + } + + protected override Sql GetBaseQuery(bool isCount) + { + var sql = new Sql(); + sql.Select(isCount ? "COUNT(*)" : "*") + .From() + .Where(x => x.NodeObjectType == NodeObjectTypeId); + return sql; + } + + protected override string GetBaseWhereClause() + { + return "umbracoNode.id = @Id"; + } + + protected override IEnumerable GetDeleteClauses() + { + var list = new[] + { + "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id", + "DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id", + "DELETE FROM umbracoRelation WHERE parentId = @Id", + "DELETE FROM umbracoRelation WHERE childId = @Id", + "DELETE FROM cmsTagRelationship WHERE nodeId = @Id", + "DELETE FROM cmsMember2MemberGroup WHERE MemberGroup = @Id", + "DELETE FROM umbracoNode WHERE id = @Id" + }; + return list; + } + + protected override Guid NodeObjectTypeId + { + get { return new Guid(Constants.ObjectTypes.MemberGroup); } + } + + protected override void PersistNewItem(IMemberGroup entity) + { + //Save to db + var group = (MemberGroup)entity; + group.AddingEntity(); + var dto = _modelFactory.BuildDto(group); + var o = Database.IsNew(dto) ? Convert.ToInt32(Database.Insert(dto)) : Database.Update(dto); + + //Update with new correct path + dto.Path = string.Concat("-1,", dto.NodeId); + Database.Update(dto); + + group.ResetDirtyProperties(); + } + + protected override void PersistUpdatedItem(IMemberGroup entity) + { + var dto = _modelFactory.BuildDto(entity); + + Database.Update(dto); + + ((ICanBeDirty)entity).ResetDirtyProperties(); + } + + public IMemberGroup GetByName(string name) + { + return _cacheHelper.RuntimeCache.GetCacheItem( + string.Format("{0}.{1}", typeof (IMemberGroup).FullName, name), + () => + { + var qry = new Query().Where(group => group.Name.Equals(name)); + var result = GetByQuery(qry); + return result.FirstOrDefault(); + }, + //cache for 5 mins since that is the default in the RuntimeCacheProvider + TimeSpan.FromMinutes(5), + //sliding is true + true); + } + + public IMemberGroup CreateIfNotExists(string roleName) + { + using (var transaction = Database.GetTransaction()) + { + var qry = new Query().Where(group => group.Name.Equals(roleName)); + var result = GetByQuery(qry); + + if (result.Any()) return null; + + var grp = new MemberGroup + { + Name = roleName + }; + PersistNewItem(grp); + + if (SavingMemberGroup.IsRaisedEventCancelled(new SaveEventArgs(grp), this)) + { + return null; + } + + transaction.Complete(); + + SavedMemberGroup.RaiseEvent(new SaveEventArgs(grp), this); + + return grp; + } + } + + public IEnumerable GetMemberGroupsForMember(int memberId) + { + var sql = new Sql(); + sql.Select("umbracoNode.*") + .From() + .InnerJoin() + .On(dto => dto.NodeId, dto => dto.MemberGroup) + .Where(x => x.NodeObjectType == NodeObjectTypeId) + .Where(x => x.Member == memberId); + + return Database.Fetch(sql) + .DistinctBy(dto => dto.NodeId) + .Select(x => _modelFactory.BuildEntity(x)); + } + + public IEnumerable GetMemberGroupsForMember(string username) + { + //find the member by username + var memberSql = new Sql(); + var memberObjectType = new Guid(Constants.ObjectTypes.Member); + var escapedUsername = PetaPocoExtensions.EscapeAtSymbols(username); + memberSql.Select("umbracoNode.id") + .From() + .InnerJoin() + .On(dto => dto.NodeId, dto => dto.NodeId) + .Where(x => x.NodeObjectType == memberObjectType) + .Where(x => x.LoginName == escapedUsername); + var memberIdUsername = Database.Fetch(memberSql).FirstOrDefault(); + if (memberIdUsername.HasValue == false) + { + return Enumerable.Empty(); + } + + var sql = new Sql(); + sql.Select("umbracoNode.*") + .From() + .InnerJoin() + .On(dto => dto.NodeId, dto => dto.MemberGroup) + .Where(x => x.NodeObjectType == NodeObjectTypeId) + .Where(x => x.Member == memberIdUsername.Value); + + return Database.Fetch(sql) + .DistinctBy(dto => dto.NodeId) + .Select(x => _modelFactory.BuildEntity(x)); + } + + public void AssignRoles(string[] usernames, string[] roleNames) + { + using (var transaction = Database.GetTransaction()) + { + //first get the member ids based on the usernames + var memberSql = new Sql(); + var memberObjectType = new Guid(Constants.ObjectTypes.Member); + memberSql.Select("umbracoNode.id") + .From() + .InnerJoin() + .On(dto => dto.NodeId, dto => dto.NodeId) + .Where(x => x.NodeObjectType == memberObjectType) + .Where("cmsMember.LoginName in (@usernames)", new { usernames = usernames }); + var memberIds = Database.Fetch(memberSql).ToArray(); + + AssignRolesInternal(memberIds, roleNames); + transaction.Complete(); + } + } + + public void DissociateRoles(string[] usernames, string[] roleNames) + { + using (var transaction = Database.GetTransaction()) + { + //first get the member ids based on the usernames + var memberSql = new Sql(); + var memberObjectType = new Guid(Constants.ObjectTypes.Member); + memberSql.Select("umbracoNode.id") + .From() + .InnerJoin() + .On(dto => dto.NodeId, dto => dto.NodeId) + .Where(x => x.NodeObjectType == memberObjectType) + .Where("cmsMember.LoginName in (@usernames)", new { usernames = usernames }); + var memberIds = Database.Fetch(memberSql).ToArray(); + + DissociateRolesInternal(memberIds, roleNames); + transaction.Complete(); + } + } + + public void AssignRoles(int[] memberIds, string[] roleNames) + { + using (var transaction = Database.GetTransaction()) + { + AssignRolesInternal(memberIds, roleNames); + transaction.Complete(); + } + } + + public void AssignRolesInternal(int[] memberIds, string[] roleNames) + { + //create the missing roles first + + var existingSql = new Sql() + .Select("*") + .From() + .Where(dto => dto.NodeObjectType == NodeObjectTypeId) + .Where("umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text") + " in (@names)", new { names = roleNames }); + var existingRoles = Database.Fetch(existingSql).Select(x => x.Text); + var missingRoles = roleNames.Except(existingRoles); + var missingGroups = missingRoles.Select(x => new MemberGroup {Name = x}).ToArray(); + + if (SavingMemberGroup.IsRaisedEventCancelled(new SaveEventArgs(missingGroups), this)) + { + return; + } + foreach (var m in missingGroups) + { + PersistNewItem(m); + } + SavedMemberGroup.RaiseEvent(new SaveEventArgs(missingGroups), this); + + //now go get all the dto's for roles with these role names + var rolesForNames = Database.Fetch(existingSql).ToArray(); + + //get the groups that are currently assigned to any of these members + + var assignedSql = new Sql(); + assignedSql.Select(string.Format( + "{0},{1},{2}", + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text"), + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("Member"), + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("MemberGroup"))) + .From() + .InnerJoin() + .On(dto => dto.NodeId, dto => dto.MemberGroup) + .Where(x => x.NodeObjectType == NodeObjectTypeId) + .Where("cmsMember2MemberGroup.Member in (@ids)", new { ids = memberIds }); + + var currentlyAssigned = Database.Fetch(assignedSql).ToArray(); + + //assign the roles for each member id + + foreach (var memberId in memberIds) + { + //find any roles for the current member that are currently assigned that + //exist in the roleNames list, then determine which ones are not currently assigned. + var mId = memberId; + var found = currentlyAssigned.Where(x => x.MemberId == mId).ToArray(); + var assignedRoles = found.Where(x => roleNames.Contains(x.RoleName)).Select(x => x.RoleName); + var nonAssignedRoles = roleNames.Except(assignedRoles); + foreach (var toAssign in nonAssignedRoles) + { + var groupId = rolesForNames.First(x => x.Text == toAssign).NodeId; + Database.Insert(new Member2MemberGroupDto { Member = mId, MemberGroup = groupId }); + } + } + } + + public void DissociateRoles(int[] memberIds, string[] roleNames) + { + using (var transaction = Database.GetTransaction()) + { + DissociateRolesInternal(memberIds, roleNames); + transaction.Complete(); + } + } + + private void DissociateRolesInternal(int[] memberIds, string[] roleNames) + { + var existingSql = new Sql() + .Select("*") + .From() + .Where(dto => dto.NodeObjectType == NodeObjectTypeId) + .Where("umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text") + " in (@names)", new { names = roleNames }); + var existingRolesIds = Database.Fetch(existingSql).Select(x => x.NodeId).ToArray(); + + Database.Execute("DELETE FROM cmsMember2MemberGroup WHERE Member IN (@memberIds) AND MemberGroup IN (@memberGroups)", + new { memberIds = memberIds, memberGroups = existingRolesIds }); + } + + private class AssignedRolesDto + { + [Column("text")] + public string RoleName { get; set; } + + [Column("Member")] + public int MemberId { get; set; } + + [Column("MemberGroup")] + public int MemberGroupId { get; set; } + } + + /// + /// Occurs before Save + /// + internal static event TypedEventHandler> SavingMemberGroup; + + /// + /// Occurs after Save + /// + internal static event TypedEventHandler> SavedMemberGroup; } } \ 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 24da2e9848..d32867e5ae 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -24,23 +24,28 @@ namespace Umbraco.Core.Persistence.Repositories { private readonly IMemberTypeRepository _memberTypeRepository; private readonly ITagsRepository _tagRepository; + private readonly IMemberGroupRepository _memberGroupRepository; public MemberRepository(IDatabaseUnitOfWork work, IMemberTypeRepository memberTypeRepository, ITagsRepository tagRepository) + public MemberRepository(IDatabaseUnitOfWork work, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository) : base(work) { if (memberTypeRepository == null) throw new ArgumentNullException("memberTypeRepository"); if (tagRepository == null) throw new ArgumentNullException("tagRepository"); _memberTypeRepository = memberTypeRepository; _tagRepository = tagRepository; + _memberGroupRepository = memberGroupRepository; } public MemberRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IMemberTypeRepository memberTypeRepository, ITagsRepository tagRepository) + public MemberRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository) : base(work, cache) { if (memberTypeRepository == null) throw new ArgumentNullException("memberTypeRepository"); if (tagRepository == null) throw new ArgumentNullException("tagRepository"); _memberTypeRepository = memberTypeRepository; _tagRepository = tagRepository; + _memberGroupRepository = memberGroupRepository; } #region Overrides of RepositoryBase @@ -242,7 +247,12 @@ namespace Umbraco.Core.Persistence.Repositories //Create the PropertyData for this version - cmsPropertyData var propertyFactory = new PropertyFactory(entity.ContentType, entity.Version, entity.Id); - var propertyDataDtos = propertyFactory.BuildDto(((Member)entity).Properties); + //Add Properties + // - don't try to save the property if it doesn't exist (or doesn't have an ID) on the content type + // - this can occur if the member type doesn't contain the built-in properties that the + // - member object contains. + var propsToPersist = entity.Properties.Where(x => x.PropertyType.HasIdentity).ToArray(); + var propertyDataDtos = propertyFactory.BuildDto(propsToPersist); var keyDictionary = new Dictionary(); //Add Properties @@ -253,7 +263,7 @@ namespace Umbraco.Core.Persistence.Repositories } //Update Properties with its newly set Id - foreach (var property in ((Member)entity).Properties) + foreach (var property in propsToPersist) { property.Id = keyDictionary[property.PropertyTypeId]; } @@ -338,12 +348,10 @@ namespace Umbraco.Core.Persistence.Repositories //Add Properties // - don't try to save the property if it doesn't exist (or doesn't have an ID) on the content type // - this can occur if the member type doesn't contain the built-in properties that the - // - member object contains. - var existingProperties = entity.Properties - .Where(property => entity.ContentType.PropertyTypes.Any(x => x.Alias == property.Alias && x.HasIdentity)) - .ToList(); + // - member object contains. + var propsToPersist = entity.Properties.Where(x => x.PropertyType.HasIdentity).ToArray(); - var propertyDataDtos = propertyFactory.BuildDto(existingProperties); + var propertyDataDtos = propertyFactory.BuildDto(propsToPersist); foreach (var propertyDataDto in propertyDataDtos) { @@ -428,6 +436,57 @@ namespace Umbraco.Core.Persistence.Repositories #endregion + public IEnumerable FindMembersInRole(string roleName, string usernameToMatch, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith) + { + //get the group id + var grpQry = new Query().Where(group => group.Name.Equals(roleName)); + var memberGroup = _memberGroupRepository.GetByQuery(grpQry).FirstOrDefault(); + if (memberGroup == null) return Enumerable.Empty(); + + // get the members by username + var query = new Query(); + switch (matchType) + { + case StringPropertyMatchType.Exact: + query.Where(member => member.Username.Equals(usernameToMatch)); + break; + case StringPropertyMatchType.Contains: + query.Where(member => member.Username.Contains(usernameToMatch)); + break; + case StringPropertyMatchType.StartsWith: + query.Where(member => member.Username.StartsWith(usernameToMatch)); + break; + case StringPropertyMatchType.EndsWith: + query.Where(member => member.Username.EndsWith(usernameToMatch)); + break; + case StringPropertyMatchType.Wildcard: + query.Where(member => member.Username.SqlWildcard(usernameToMatch, TextColumnType.NVarchar)); + break; + default: + throw new ArgumentOutOfRangeException("matchType"); + } + var matchedMembers = GetByQuery(query).ToArray(); + + var membersInGroup = new List(); + //then we need to filter the matched members that are in the role + //since the max sql params are 2100 on sql server, we'll reduce that to be safe for potentially other servers and run the queries in batches + var inGroups = matchedMembers.InGroupsOf(1000); + foreach (var batch in inGroups) + { + var memberIdBatch = batch.Select(x => x.Id); + var sql = new Sql().Select("*").From() + .Where(dto => dto.MemberGroup == memberGroup.Id) + .Where("Member IN (@memberIds)", new { memberIds = memberIdBatch }); + var memberIdsInGroup = Database.Fetch(sql) + .Select(x => x.Member).ToArray(); + + membersInGroup.AddRange(matchedMembers.Where(x => memberIdsInGroup.Contains(x.Id))); + } + + return membersInGroup; + + } + /// /// Get all members in a specific group /// @@ -435,9 +494,13 @@ namespace Umbraco.Core.Persistence.Repositories /// public IEnumerable GetByMemberGroup(string groupName) { - var subquery = GetSubquery().Where(x => x.Text == groupName); + var grpQry = new Query().Where(group => group.Name.Equals(groupName)); + var memberGroup = _memberGroupRepository.GetByQuery(grpQry).FirstOrDefault(); + if (memberGroup == null) return Enumerable.Empty(); + var subQuery = new Sql().Select("Member").From().Where(dto => dto.MemberGroup == memberGroup.Id); + var sql = GetBaseQuery(false) - .Append(new Sql("WHERE umbracoNode.id IN (" + subquery.SQL + ")", subquery.Arguments)) + .Append(new Sql("WHERE umbracoNode.id IN (" + subQuery.SQL + ")", subQuery.Arguments)) .OrderByDescending(x => x.VersionDate) .OrderBy(x => x.SortOrder); diff --git a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs index bd0d8bb175..08d22ff3f6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs @@ -1,14 +1,18 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Dynamic; using System.Globalization; using System.Linq; using System.Text; +using System.Web.Caching; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; +using CacheKeys = Umbraco.Core.Cache.CacheKeys; +using Umbraco.Core.Cache; namespace Umbraco.Core.Persistence.Repositories { @@ -20,10 +24,12 @@ namespace Umbraco.Core.Persistence.Repositories where TEntity : class, IAggregateRoot { private readonly IDatabaseUnitOfWork _unitOfWork; + private readonly CacheHelper _cache; - internal PermissionRepository(IDatabaseUnitOfWork unitOfWork) + internal PermissionRepository(IDatabaseUnitOfWork unitOfWork, CacheHelper cache) { _unitOfWork = unitOfWork; + _cache = cache; } /// @@ -32,39 +38,57 @@ namespace Umbraco.Core.Persistence.Repositories /// /// /// - internal IEnumerable GetUserPermissionsForEntities(object userId, params int[] entityIds) + internal IEnumerable GetUserPermissionsForEntities(int userId, params int[] entityIds) + { + var entityIdKey = string.Join(",", entityIds.Select(x => x.ToString(CultureInfo.InvariantCulture))); + return _cache.RuntimeCache.GetCacheItem>( + string.Format("{0}{1}{2}", CacheKeys.UserPermissionsCacheKey, userId, entityIdKey), + () => { - var whereBuilder = new StringBuilder(); + + var whereBuilder = new StringBuilder(); - //where userId = @userId AND - whereBuilder.Append(SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("userId")); - whereBuilder.Append("="); - whereBuilder.Append(userId); - whereBuilder.Append(" AND "); + //where userId = @userId AND + whereBuilder.Append(SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("userId")); + whereBuilder.Append("="); + whereBuilder.Append(userId); - //where nodeId = @nodeId1 OR nodeId = @nodeId2, etc... - whereBuilder.Append("("); - for (var index = 0; index < entityIds.Length; index++) - { - var entityId = entityIds[index]; - whereBuilder.Append(SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("nodeId")); - whereBuilder.Append("="); - whereBuilder.Append(entityId); - if (index < entityIds.Length - 1) - { - whereBuilder.Append(" OR "); - } - } - whereBuilder.Append(")"); + if (entityIds.Any()) + { + whereBuilder.Append(" AND "); - var sql = new Sql(); - sql.Select("*") - .From() - .Where(whereBuilder.ToString()); + //where nodeId = @nodeId1 OR nodeId = @nodeId2, etc... + whereBuilder.Append("("); + for (var index = 0; index < entityIds.Length; index++) + { + var entityId = entityIds[index]; + whereBuilder.Append(SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("nodeId")); + whereBuilder.Append("="); + whereBuilder.Append(entityId); + if (index < entityIds.Length - 1) + { + whereBuilder.Append(" OR "); + } + } + whereBuilder.Append(")"); + } + + var sql = new Sql(); + sql.Select("*") + .From() + .Where(whereBuilder.ToString()); + + //ToArray() to ensure it's all fetched from the db once. + var result = _unitOfWork.Database.Fetch(sql).ToArray(); + return ConvertToPermissionList(result); + + }, + //Since this cache can be quite large (http://issues.umbraco.org/issue/U4-2161) we will only have this exist in cache for 20 minutes, + // then it will refresh from the database. + new TimeSpan(0, 20, 0), + //Since this cache can be quite large (http://issues.umbraco.org/issue/U4-2161) we will make this priority below average + priority: CacheItemPriority.BelowNormal); - //ToArray() to ensure it's all fetched from the db once. - var result = _unitOfWork.Database.Fetch(sql).ToArray(); - return ConvertToPermissionList(result); } /// diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs index b97227d783..097473a891 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs @@ -58,7 +58,7 @@ namespace Umbraco.Core.Persistence.Repositories /// public void AddOrUpdate(TEntity entity) { - if (!entity.HasIdentity) + if (entity.HasIdentity == false) { _work.RegisterAdded(entity, this); } @@ -104,7 +104,7 @@ namespace Umbraco.Core.Persistence.Repositories { //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 - TracksChangesEntityBase asEntity = entity as TracksChangesEntityBase; + var asEntity = entity as TracksChangesEntityBase; if (asEntity != null) { asEntity.ResetDirtyProperties(false); @@ -135,13 +135,16 @@ namespace Umbraco.Core.Persistence.Repositories { if (ids.Any()) { - var entities = _cache.GetByIds(typeof(TEntity), ids.Select(id => id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString())).ToList()); + var entities = _cache.GetByIds( + typeof (TEntity), ids.Select(id => id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString())).ToList()) + .ToArray(); + if (ids.Count().Equals(entities.Count()) && entities.Any(x => x == null) == false) return entities.Select(x => (TEntity)x); } else { - var allEntities = _cache.GetAllByType(typeof(TEntity)); + var allEntities = _cache.GetAllByType(typeof (TEntity)).ToArray(); if (allEntities.Any()) { @@ -154,7 +157,7 @@ namespace Umbraco.Core.Persistence.Repositories } } - var entityCollection = PerformGetAll(ids); + var entityCollection = PerformGetAll(ids).ToArray(); foreach (var entity in entityCollection) { diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index 9c6c742170..f08e155622 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -22,17 +22,20 @@ namespace Umbraco.Core.Persistence.Repositories internal class UserRepository : PetaPocoRepositoryBase, IUserRepository { private readonly IUserTypeRepository _userTypeRepository; + private readonly CacheHelper _cacheHelper; - public UserRepository(IDatabaseUnitOfWork work, IUserTypeRepository userTypeRepository) + public UserRepository(IDatabaseUnitOfWork work, IUserTypeRepository userTypeRepository, CacheHelper cacheHelper) : base(work) { _userTypeRepository = userTypeRepository; + _cacheHelper = cacheHelper; } - public UserRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IUserTypeRepository userTypeRepository) + public UserRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IUserTypeRepository userTypeRepository, CacheHelper cacheHelper) : base(work, cache) { _userTypeRepository = userTypeRepository; + _cacheHelper = cacheHelper; } #region Overrides of RepositoryBase @@ -315,9 +318,9 @@ namespace Umbraco.Core.Persistence.Repositories /// /// /// - public IEnumerable GetUserPermissionsForEntities(object userId, params int[] entityIds) + public IEnumerable GetUserPermissionsForEntities(int userId, params int[] entityIds) { - var repo = new PermissionRepository(UnitOfWork); + var repo = new PermissionRepository(UnitOfWork, _cacheHelper); return repo.GetUserPermissionsForEntities(userId, entityIds); } diff --git a/src/Umbraco.Core/Persistence/RepositoryFactory.cs b/src/Umbraco.Core/Persistence/RepositoryFactory.cs index 146093566d..19d771d194 100644 --- a/src/Umbraco.Core/Persistence/RepositoryFactory.cs +++ b/src/Umbraco.Core/Persistence/RepositoryFactory.cs @@ -1,4 +1,5 @@ using Umbraco.Core.Configuration; +using System; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Persistence.Caching; using Umbraco.Core.Persistence.Repositories; @@ -12,6 +13,7 @@ namespace Umbraco.Core.Persistence public class RepositoryFactory { private readonly bool _disableAllCache; + private readonly CacheHelper _cacheHelper; private readonly IUmbracoSettingsSection _settings; public RepositoryFactory() @@ -20,6 +22,20 @@ namespace Umbraco.Core.Persistence } + public RepositoryFactory(CacheHelper cacheHelper) + { + if (cacheHelper == null) throw new ArgumentNullException("cacheHelper"); + _disableAllCache = false; + _cacheHelper = cacheHelper; + } + + public RepositoryFactory(bool disableAllCache, CacheHelper cacheHelper) + { + if (cacheHelper == null) throw new ArgumentNullException("cacheHelper"); + _disableAllCache = disableAllCache; + _cacheHelper = cacheHelper; + } + public RepositoryFactory(bool disableAllCache) : this(disableAllCache, UmbracoConfig.For.UmbracoSettings()) { @@ -30,6 +46,7 @@ namespace Umbraco.Core.Persistence { _disableAllCache = disableAllCache; _settings = settings; + _cacheHelper = disableAllCache ? CacheHelper.CreateDisabledCacheHelper() : ApplicationContext.Current.ApplicationCache; } @@ -48,13 +65,14 @@ namespace Umbraco.Core.Persistence CreateContentTypeRepository(uow), CreateTemplateRepository(uow), CreateTagsRepository(uow)) { EnsureUniqueNaming = _settings.Content.EnsureUniqueNaming }; + _cacheHelper) { EnsureUniqueNaming = Configuration.UmbracoSettings.EnsureUniqueNaming }; } public virtual IContentTypeRepository CreateContentTypeRepository(IDatabaseUnitOfWork uow) { return new ContentTypeRepository( uow, - _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : InMemoryCacheProvider.Current, + _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current, new TemplateRepository(uow, NullCacheProvider.Current)); } @@ -69,7 +87,7 @@ namespace Umbraco.Core.Persistence { return new DictionaryRepository( uow, - _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : InMemoryCacheProvider.Current, + _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current, CreateLanguageRepository(uow)); } @@ -77,7 +95,7 @@ namespace Umbraco.Core.Persistence { return new LanguageRepository( uow, - _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : InMemoryCacheProvider.Current); + _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current); } public virtual IMediaRepository CreateMediaRepository(IDatabaseUnitOfWork uow) @@ -93,7 +111,7 @@ namespace Umbraco.Core.Persistence { return new MediaTypeRepository( uow, - _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : InMemoryCacheProvider.Current); + _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current); } public virtual IRelationRepository CreateRelationRepository(IDatabaseUnitOfWork uow) @@ -142,11 +160,13 @@ namespace Umbraco.Core.Persistence } internal virtual IUserRepository CreateUserRepository(IDatabaseUnitOfWork uow) - { + { return new UserRepository( uow, - NullCacheProvider.Current, - CreateUserTypeRepository(uow)); + //Need to cache users - we look up user information more than anything in the back office! + RuntimeCacheProvider.Current, + CreateUserTypeRepository(uow), + _cacheHelper); } internal virtual IMacroRepository CreateMacroRepository(IDatabaseUnitOfWork uow) @@ -160,11 +180,21 @@ namespace Umbraco.Core.Persistence _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current, CreateMemberTypeRepository(uow), CreateTagsRepository(uow)); + return new MemberRepository( + uow, + _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current, + CreateMemberTypeRepository(uow), + CreateMemberGroupRepository(uow)); } internal virtual IMemberTypeRepository CreateMemberTypeRepository(IDatabaseUnitOfWork uow) { - return new MemberTypeRepository(uow, _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : InMemoryCacheProvider.Current); + return new MemberTypeRepository(uow, _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current); + } + + internal virtual IMemberGroupRepository CreateMemberGroupRepository(IDatabaseUnitOfWork uow) + { + return new MemberGroupRepository(uow, _disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current, _cacheHelper); } internal virtual IEntityRepository CreateEntityRepository(IDatabaseUnitOfWork uow) diff --git a/src/Umbraco.Core/Security/IUsersMembershipProvider.cs b/src/Umbraco.Core/Security/IUsersMembershipProvider.cs new file mode 100644 index 0000000000..8f2dbf8b00 --- /dev/null +++ b/src/Umbraco.Core/Security/IUsersMembershipProvider.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Core.Security +{ + /// + /// A marker interface used internally to identify Umbraco built-in Users membership providers + /// + internal interface IUsersMembershipProvider + { + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Security/MembershipProviderExtensions.cs b/src/Umbraco.Core/Security/MembershipProviderExtensions.cs index 85db27ac97..ed64f179b7 100644 --- a/src/Umbraco.Core/Security/MembershipProviderExtensions.cs +++ b/src/Umbraco.Core/Security/MembershipProviderExtensions.cs @@ -10,6 +10,16 @@ namespace Umbraco.Core.Security { internal static class MembershipProviderExtensions { + /// + /// Returns true if the provider specified is a built-in Umbraco users provider + /// + /// + /// + public static bool IsUmbracoUsersProvider(this MembershipProvider membershipProvider) + { + return (membershipProvider is IUsersMembershipProvider); + } + /// /// Returns true if the provider specified is a built-in Umbraco membership provider /// @@ -20,6 +30,8 @@ namespace Umbraco.Core.Security return (membershipProvider is UmbracoMembershipProviderBase); } + //TODO: Add role provider checks too + public static UmbracoMembershipProviderBase AsUmbracoMembershipProvider(this MembershipProvider membershipProvider) { return (UmbracoMembershipProviderBase)membershipProvider; diff --git a/src/Umbraco.Core/Security/UmbracoMembershipProviderBase.cs b/src/Umbraco.Core/Security/UmbracoMembershipProviderBase.cs index 6cbd12f448..12ca2b4b4a 100644 --- a/src/Umbraco.Core/Security/UmbracoMembershipProviderBase.cs +++ b/src/Umbraco.Core/Security/UmbracoMembershipProviderBase.cs @@ -3,7 +3,6 @@ using System.Web.Security; namespace Umbraco.Core.Security { - /// /// A base membership provider class for umbraco providers /// diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 2dcd34e451..f4956329ae 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -628,10 +628,10 @@ namespace Umbraco.Core.Services /// The to publish /// Optional Id of the User issueing the publishing /// True if publishing succeeded, otherwise False - [Obsolete("Use PublishWithStatus instead, that method will provide more detailed information on the outcome")] public bool Publish(IContent content, int userId = 0) { var result = SaveAndPublishDo(content, userId); + LogHelper.Info("Call was made to ContentService.Publish, use PublishWithStatus instead since that method will provide more detailed information on the outcome"); return result.Success; } diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 0a19deda6e..9acdef9bc3 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -249,7 +249,6 @@ namespace Umbraco.Core.Services /// The to publish /// Optional Id of the User issueing the publishing /// True if publishing succeeded, otherwise False - [Obsolete("Use PublishWithStatus instead, that method will provide more detailed information on the outcome")] bool Publish(IContent content, int userId = 0); /// diff --git a/src/Umbraco.Core/Services/IMemberGroupService.cs b/src/Umbraco.Core/Services/IMemberGroupService.cs new file mode 100644 index 0000000000..966bb87e4c --- /dev/null +++ b/src/Umbraco.Core/Services/IMemberGroupService.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Services +{ + public interface IMemberGroupService : IService + { + IEnumerable GetAll(); + IMemberGroup GetById(int id); + IMemberGroup GetByName(string name); + void Save(IMemberGroup memberGroup, bool raiseEvents = true); + void Delete(IMemberGroup memberGroup); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IMemberService.cs b/src/Umbraco.Core/Services/IMemberService.cs index 87c15573c6..1c66640cd1 100644 --- a/src/Umbraco.Core/Services/IMemberService.cs +++ b/src/Umbraco.Core/Services/IMemberService.cs @@ -8,7 +8,7 @@ namespace Umbraco.Core.Services /// /// Defines the MemberService, which is an easy access to operations involving (umbraco) members. /// - internal interface IMemberService : IMembershipMemberService + public interface IMemberService : IMembershipMemberService { /// /// Checks if a member with the id exists @@ -16,14 +16,7 @@ namespace Umbraco.Core.Services /// /// bool Exists(int id); - - /// - /// Get a member by its id - /// - /// - /// - IMember GetById(int id); - + /// /// Get a member by the unique key /// @@ -65,6 +58,17 @@ namespace Umbraco.Core.Services /// void DeleteMembersOfType(int memberTypeId); + /// + /// Find members based on their display name + /// + /// + /// + /// + /// + /// + /// + IEnumerable FindMembersByDisplayName(string displayNameToMatch, int pageIndex, int pageSize, out int totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith); + /// /// Get members based on a property search /// diff --git a/src/Umbraco.Core/Services/IMemberTypeService.cs b/src/Umbraco.Core/Services/IMemberTypeService.cs index b2aca1bf54..b2f298d822 100644 --- a/src/Umbraco.Core/Services/IMemberTypeService.cs +++ b/src/Umbraco.Core/Services/IMemberTypeService.cs @@ -3,28 +3,28 @@ using Umbraco.Core.Models; namespace Umbraco.Core.Services { - internal interface IMemberTypeService : IService - { + public interface IMemberTypeService : IService + { /// /// Gets a list of all available objects /// /// Optional list of ids /// An Enumerable list of objects - IEnumerable GetAllMemberTypes(params int[] ids); + IEnumerable GetAll(params int[] ids); /// /// Gets an object by its Id /// /// Id of the to retrieve /// - IMemberType GetMemberType(int id); + IMemberType Get(int id); /// /// Gets an object by its Alias /// /// Alias of the to retrieve /// - IMemberType GetMemberType(string alias); + IMemberType Get(string alias); /// /// Saves a single object diff --git a/src/Umbraco.Core/Services/IMembershipMemberService.cs b/src/Umbraco.Core/Services/IMembershipMemberService.cs index 026615ef11..74e9046c15 100644 --- a/src/Umbraco.Core/Services/IMembershipMemberService.cs +++ b/src/Umbraco.Core/Services/IMembershipMemberService.cs @@ -6,16 +6,15 @@ using Umbraco.Core.Persistence.Querying; namespace Umbraco.Core.Services { - /// /// Defines part of the MemberService, which is specific to methods used by the membership provider. /// /// /// Idea is to have this is an isolated interface so that it can be easily 'replaced' in the membership provider impl. /// - internal interface IMembershipMemberService : IMembershipMemberService + public interface IMembershipMemberService : IMembershipMemberService, IMembershipRoleService { - IMember CreateMember(string email, string username, string password, IMemberType memberType); + IMember CreateMemberWithIdentity(string username, string email, string password, IMemberType memberType, bool raiseEvents = true); } /// @@ -24,7 +23,7 @@ namespace Umbraco.Core.Services /// /// Idea is to have this is an isolated interface so that it can be easily 'replaced' in the membership provider impl. /// - internal interface IMembershipMemberService : IService + public interface IMembershipMemberService : IService where T : class, IMembershipUser { /// @@ -47,10 +46,11 @@ namespace Umbraco.Core.Services /// /// /// + /// /// - T CreateMember(string username, string email, string password, string memberTypeAlias); + T CreateMemberWithIdentity(string username, string email, string password, string memberTypeAlias, bool raiseEvents = true); - T GetById(object id); + T GetById(int id); /// /// Get a member by email diff --git a/src/Umbraco.Core/Services/IMembershipRoleService.cs b/src/Umbraco.Core/Services/IMembershipRoleService.cs new file mode 100644 index 0000000000..46860e8c74 --- /dev/null +++ b/src/Umbraco.Core/Services/IMembershipRoleService.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Persistence.Querying; + +namespace Umbraco.Core.Services +{ + public interface IMembershipRoleService + where T : class, IMembershipUser + { + void AddRole(string roleName); + IEnumerable GetAllRoles(); + IEnumerable GetAllRoles(int memberId); + IEnumerable GetAllRoles(string username); + IEnumerable GetMembersInRole(string roleName); + IEnumerable FindMembersInRole(string roleName, string usernameToMatch, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith); + bool DeleteRole(string roleName, bool throwIfBeingUsed); + void AssignRoles(string[] usernames, string[] roleNames); + void DissociateRoles(string[] usernames, string[] roleNames); + void AssignRoles(int[] memberIds, string[] roleNames); + void DissociateRoles(int[] memberIds, string[] roleNames); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IMembershipUserService.cs b/src/Umbraco.Core/Services/IMembershipUserService.cs index 5a5464bc1f..a04d9e23a6 100644 --- a/src/Umbraco.Core/Services/IMembershipUserService.cs +++ b/src/Umbraco.Core/Services/IMembershipUserService.cs @@ -8,10 +8,10 @@ namespace Umbraco.Core.Services /// /// Idea is to have this is an isolated interface so that it can be easily 'replaced' in the membership provider impl. /// - internal interface IMembershipUserService : IMembershipMemberService + public interface IMembershipUserService : IMembershipMemberService { - IUser CreateMember(string username, string email, string password, IUserType userType); + IUser CreateMemberWithIdentity(string username, string email, string password, IUserType userType, bool raiseEvents = true); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IUserService.cs b/src/Umbraco.Core/Services/IUserService.cs index 11d0203ab0..644326a1d4 100644 --- a/src/Umbraco.Core/Services/IUserService.cs +++ b/src/Umbraco.Core/Services/IUserService.cs @@ -7,18 +7,56 @@ namespace Umbraco.Core.Services /// /// Defines the UserService, which is an easy access to operations involving and eventually Users. /// - internal interface IUserService : IMembershipUserService + public interface IUserService : IMembershipUserService { + /// + /// To permanently delete the user pass in true, otherwise they will just be disabled + /// + /// + /// + void Delete(IUser user, bool deletePermanently); + /// /// Gets an IProfile by User Id. /// /// Id of the User to retrieve /// IProfile GetProfileById(int id); + + /// + /// Get profile by username + /// + /// + /// IProfile GetProfileByUserName(string username); + /// + /// Get user by Id + /// + /// + /// IUser GetUserById(int id); + /// + /// This is useful when an entire section is removed from config + /// + /// + void DeleteSectionFromAllUsers(string sectionAlias); + + /// + /// Get permissions set for user and specified node ids + /// + /// + /// + /// Specifiying nothing will return all user permissions for all nodes + /// + /// + IEnumerable GetPermissions(IUser user, params int[] nodeIds); + + #region User types + + IEnumerable GetAllUserTypes(params int[] ids); + /// /// Gets an IUserType by its Alias /// @@ -26,6 +64,13 @@ namespace Umbraco.Core.Services /// IUserType GetUserTypeByAlias(string alias); + /// + /// Gets an IUserType by its Id + /// + /// + /// + IUserType GetUserTypeById(int id); + /// /// Gets an IUserType by its Name /// @@ -45,19 +90,7 @@ namespace Umbraco.Core.Services /// /// void DeleteUserType(IUserType userType); - - /// - /// This is useful when an entire section is removed from config - /// - /// - void DeleteSectionFromAllUsers(string sectionAlias); - /// - /// Returns a list of the sections that the user is allowed access to - /// - /// - IEnumerable GetUserSections(IUser user); - - IEnumerable GetPermissions(IUser user, params int[] nodeIds); + #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/MemberGroupService.cs b/src/Umbraco.Core/Services/MemberGroupService.cs new file mode 100644 index 0000000000..ae12614d56 --- /dev/null +++ b/src/Umbraco.Core/Services/MemberGroupService.cs @@ -0,0 +1,138 @@ +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Events; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.UnitOfWork; + +namespace Umbraco.Core.Services +{ + public class MemberGroupService : IMemberGroupService + { + private readonly RepositoryFactory _repositoryFactory; + private readonly IDatabaseUnitOfWorkProvider _uowProvider; + + public MemberGroupService(RepositoryFactory repositoryFactory) + : this(new PetaPocoUnitOfWorkProvider(), repositoryFactory) + { + } + + public MemberGroupService(IDatabaseUnitOfWorkProvider provider) + : this(provider, new RepositoryFactory()) + { + } + + public MemberGroupService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory) + { + _repositoryFactory = repositoryFactory; + _uowProvider = provider; + + //Proxy events! + MemberGroupRepository.SavedMemberGroup += MemberGroupRepository_SavedMemberGroup; + MemberGroupRepository.SavingMemberGroup += MemberGroupRepository_SavingMemberGroup; + } + + #region Proxied event handlers + void MemberGroupRepository_SavingMemberGroup(IMemberGroupRepository sender, SaveEventArgs e) + { + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(e.SavedEntities), this)) + { + e.Cancel = true; + } + } + + void MemberGroupRepository_SavedMemberGroup(IMemberGroupRepository sender, SaveEventArgs e) + { + Saved.RaiseEvent(new SaveEventArgs(e.SavedEntities, false), this); + } + #endregion + + public IEnumerable GetAll() + { + using (var repository = _repositoryFactory.CreateMemberGroupRepository(_uowProvider.GetUnitOfWork())) + { + return repository.GetAll(); + } + } + + public IMemberGroup GetById(int id) + { + using (var repository = _repositoryFactory.CreateMemberGroupRepository(_uowProvider.GetUnitOfWork())) + { + return repository.Get(id); + } + } + + public IMemberGroup GetByName(string name) + { + using (var repository = _repositoryFactory.CreateMemberGroupRepository(_uowProvider.GetUnitOfWork())) + { + return repository.GetByName(name); + } + } + + public void Save(IMemberGroup memberGroup, bool raiseEvents = true) + { + if (raiseEvents) + { + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(memberGroup), this)) + { + return; + } + } + + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMemberGroupRepository(uow)) + { + repository.AddOrUpdate(memberGroup); + uow.Commit(); + } + + if (raiseEvents) + Saved.RaiseEvent(new SaveEventArgs(memberGroup, false), this); + } + + public void Delete(IMemberGroup memberGroup) + { + if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(memberGroup), this)) + return; + + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMemberGroupRepository(uow)) + { + repository.Delete(memberGroup); + uow.Commit(); + } + + Deleted.RaiseEvent(new DeleteEventArgs(memberGroup, false), this); + } + + /// + /// Occurs before Delete of a member group + /// + public static event TypedEventHandler> Deleting; + + /// + /// Occurs after Delete of a member group + /// + public static event TypedEventHandler> Deleted; + + /// + /// Occurs before Save of a member group + /// + /// + /// We need to proxy these events because the events need to take place at the repo level + /// + public static event TypedEventHandler> Saving; + + /// + /// Occurs after Save of a member group + /// + /// + /// We need to proxy these events because the events need to take place at the repo level + /// + public static event TypedEventHandler> Saved; + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/MemberService.cs b/src/Umbraco.Core/Services/MemberService.cs index f37339c70d..024ebcaf93 100644 --- a/src/Umbraco.Core/Services/MemberService.cs +++ b/src/Umbraco.Core/Services/MemberService.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; using System.Linq; @@ -20,26 +21,31 @@ namespace Umbraco.Core.Services /// /// Represents the MemberService. /// - internal class MemberService : IMemberService + public class MemberService : IMemberService { private readonly RepositoryFactory _repositoryFactory; + private readonly IMemberGroupService _memberGroupService; private readonly IDatabaseUnitOfWorkProvider _uowProvider; private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(); - internal MemberService(RepositoryFactory repositoryFactory) - : this(new PetaPocoUnitOfWorkProvider(), repositoryFactory) + public MemberService(RepositoryFactory repositoryFactory, IMemberGroupService memberGroupService) + : this(new PetaPocoUnitOfWorkProvider(), repositoryFactory, memberGroupService) { } - internal MemberService(IDatabaseUnitOfWorkProvider provider) - : this(provider, new RepositoryFactory()) + public MemberService(IDatabaseUnitOfWorkProvider provider, IMemberGroupService memberGroupService) + : this(provider, new RepositoryFactory(), memberGroupService) { } - internal MemberService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory) + public MemberService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, IMemberGroupService memberGroupService) { + if (provider == null) throw new ArgumentNullException("provider"); + if (repositoryFactory == null) throw new ArgumentNullException("repositoryFactory"); + if (memberGroupService == null) throw new ArgumentNullException("memberGroupService"); _repositoryFactory = repositoryFactory; + _memberGroupService = memberGroupService; _uowProvider = provider; } @@ -207,6 +213,38 @@ namespace Umbraco.Core.Services } } + public IEnumerable FindMembersByDisplayName(string displayNameToMatch, int pageIndex, int pageSize, out int totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMemberRepository(uow)) + { + var query = new Query(); + + switch (matchType) + { + case StringPropertyMatchType.Exact: + query.Where(member => member.Name.Equals(displayNameToMatch)); + break; + case StringPropertyMatchType.Contains: + query.Where(member => member.Name.Contains(displayNameToMatch)); + break; + case StringPropertyMatchType.StartsWith: + query.Where(member => member.Name.StartsWith(displayNameToMatch)); + break; + case StringPropertyMatchType.EndsWith: + query.Where(member => member.Name.EndsWith(displayNameToMatch)); + break; + case StringPropertyMatchType.Wildcard: + query.Where(member => member.Name.SqlWildcard(displayNameToMatch, TextColumnType.NVarchar)); + break; + default: + throw new ArgumentOutOfRangeException("matchType"); + } + + return repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, dto => dto.Name); + } + } + /// /// Does a search for members that contain the specified string in their email address /// @@ -538,11 +576,20 @@ namespace Umbraco.Core.Services } } - public IMember CreateMember(string email, string username, string password, IMemberType memberType) + public IMember CreateMemberWithIdentity(string username, string email, string password, IMemberType memberType, bool raiseEvents = true) { if (memberType == null) throw new ArgumentNullException("memberType"); - var member = new Member(email, email, username, password, -1, memberType); + var member = new Member(username, email.ToLower().Trim(), username, password, -1, memberType); + + if (raiseEvents) + { + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(member), this)) + { + member.WasCancelled = true; + return member; + } + } var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateMemberRepository(uow)) @@ -555,6 +602,9 @@ namespace Umbraco.Core.Services CreateAndSaveMemberXml(xml, member.Id, uow.Database); } + if (raiseEvents) + Saved.RaiseEvent(new SaveEventArgs(member, false), this); + return member; } @@ -565,8 +615,9 @@ namespace Umbraco.Core.Services /// /// /// + /// /// - public IMember CreateMember(string email, string username, string password, string memberTypeAlias) + public IMember CreateMemberWithIdentity(string username, string email, string password, string memberTypeAlias, bool raiseEvents = true) { var uow = _uowProvider.GetUnitOfWork(); IMemberType memberType; @@ -582,7 +633,7 @@ namespace Umbraco.Core.Services throw new ArgumentException(string.Format("No MemberType matching the passed in Alias: '{0}' was found", memberTypeAlias)); } - return CreateMember(email, username, password, memberTypeAlias); + return CreateMemberWithIdentity(username, email, password, memberType, raiseEvents); } /// @@ -671,7 +722,10 @@ namespace Umbraco.Core.Services if (raiseEvents) { if (Saving.IsRaisedEventCancelled(new SaveEventArgs(member), this)) + { return; + } + } var uow = _uowProvider.GetUnitOfWork(); @@ -721,6 +775,130 @@ namespace Umbraco.Core.Services #endregion + #region IMembershipRoleService Implementation + + public void AddRole(string roleName) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMemberGroupRepository(uow)) + { + repository.CreateIfNotExists(roleName); + } + } + + public IEnumerable GetAllRoles() + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMemberGroupRepository(uow)) + { + var result = repository.GetAll(); + return result.Select(x => x.Name).Distinct(); + } + } + + public IEnumerable GetAllRoles(int memberId) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMemberGroupRepository(uow)) + { + var result = repository.GetMemberGroupsForMember(memberId); + return result.Select(x => x.Name).Distinct(); + } + } + + public IEnumerable GetAllRoles(string username) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMemberGroupRepository(uow)) + { + var result = repository.GetMemberGroupsForMember(username); + return result.Select(x => x.Name).Distinct(); + } + } + + public IEnumerable GetMembersInRole(string roleName) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMemberRepository(uow)) + { + return repository.GetByMemberGroup(roleName); + } + } + + public IEnumerable FindMembersInRole(string roleName, string usernameToMatch, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMemberRepository(uow)) + { + return repository.FindMembersInRole(roleName, usernameToMatch, matchType); + } + } + + public bool DeleteRole(string roleName, bool throwIfBeingUsed) + { + using (new WriteLock(Locker)) + { + if (throwIfBeingUsed) + { + var inRole = GetMembersInRole(roleName); + if (inRole.Any()) + { + throw new InvalidOperationException("The role " + roleName + " is currently assigned to members"); + } + } + + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMemberGroupRepository(uow)) + { + var qry = new Query().Where(g => g.Name == roleName); + var found = repository.GetByQuery(qry).ToArray(); + + foreach (var memberGroup in found) + { + _memberGroupService.Delete(memberGroup); + } + return found.Any(); + } + } + } + + public void AssignRoles(string[] usernames, string[] roleNames) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMemberGroupRepository(uow)) + { + repository.AssignRoles(usernames, roleNames); + } + } + + public void DissociateRoles(string[] usernames, string[] roleNames) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMemberGroupRepository(uow)) + { + repository.DissociateRoles(usernames, roleNames); + } + } + + public void AssignRoles(int[] memberIds, string[] roleNames) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMemberGroupRepository(uow)) + { + repository.AssignRoles(memberIds, roleNames); + } + } + + public void DissociateRoles(int[] memberIds, string[] roleNames) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMemberGroupRepository(uow)) + { + repository.DissociateRoles(memberIds, roleNames); + } + } + #endregion + /// /// Rebuilds all xml content in the cmsContentXml table for all media /// @@ -809,8 +987,7 @@ namespace Umbraco.Core.Services } #region Event Handlers - - + /// /// Occurs before Delete /// @@ -895,5 +1072,6 @@ namespace Umbraco.Core.Services return new Member(name, email, username, password, -1, memType); } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/MemberTypeService.cs b/src/Umbraco.Core/Services/MemberTypeService.cs index c46628041c..81a91e37b5 100644 --- a/src/Umbraco.Core/Services/MemberTypeService.cs +++ b/src/Umbraco.Core/Services/MemberTypeService.cs @@ -11,7 +11,7 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { - internal class MemberTypeService : ContentTypeServiceBase, IMemberTypeService + public class MemberTypeService : ContentTypeServiceBase, IMemberTypeService { private readonly IDatabaseUnitOfWorkProvider _uowProvider; private readonly RepositoryFactory _repositoryFactory; @@ -36,7 +36,7 @@ namespace Umbraco.Core.Services _memberService = memberService; } - public IEnumerable GetAllMemberTypes(params int[] ids) + public IEnumerable GetAll(params int[] ids) { using (var repository = _repositoryFactory.CreateMemberTypeRepository(_uowProvider.GetUnitOfWork())) { @@ -49,7 +49,7 @@ namespace Umbraco.Core.Services /// /// Id of the to retrieve /// - public IMemberType GetMemberType(int id) + public IMemberType Get(int id) { using (var repository = _repositoryFactory.CreateMemberTypeRepository(_uowProvider.GetUnitOfWork())) { @@ -62,7 +62,7 @@ namespace Umbraco.Core.Services /// /// Alias of the to retrieve /// - public IMemberType GetMemberType(string alias) + public IMemberType Get(string alias) { using (var repository = _repositoryFactory.CreateMemberTypeRepository(_uowProvider.GetUnitOfWork())) { @@ -75,7 +75,7 @@ namespace Umbraco.Core.Services public void Save(IMemberType memberType, int userId = 0) { - if (SavingMemberType.IsRaisedEventCancelled(new SaveEventArgs(memberType), this)) + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(memberType), this)) return; using (new WriteLock(Locker)) @@ -91,14 +91,14 @@ namespace Umbraco.Core.Services UpdateContentXmlStructure(memberType); } - SavedMemberType.RaiseEvent(new SaveEventArgs(memberType, false), this); + Saved.RaiseEvent(new SaveEventArgs(memberType, false), this); } public void Save(IEnumerable memberTypes, int userId = 0) { var asArray = memberTypes.ToArray(); - if (SavingMemberType.IsRaisedEventCancelled(new SaveEventArgs(asArray), this)) + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(asArray), this)) return; using (new WriteLock(Locker)) @@ -118,12 +118,12 @@ namespace Umbraco.Core.Services UpdateContentXmlStructure(asArray.Cast().ToArray()); } - SavedMemberType.RaiseEvent(new SaveEventArgs(asArray, false), this); + Saved.RaiseEvent(new SaveEventArgs(asArray, false), this); } public void Delete(IMemberType memberType, int userId = 0) { - if (DeletingMemberType.IsRaisedEventCancelled(new DeleteEventArgs(memberType), this)) + if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(memberType), this)) return; using (new WriteLock(Locker)) @@ -136,7 +136,7 @@ namespace Umbraco.Core.Services repository.Delete(memberType); uow.Commit(); - DeletedMemberType.RaiseEvent(new DeleteEventArgs(memberType, false), this); + Deleted.RaiseEvent(new DeleteEventArgs(memberType, false), this); } } } @@ -145,7 +145,7 @@ namespace Umbraco.Core.Services { var asArray = memberTypes.ToArray(); - if (DeletingMemberType.IsRaisedEventCancelled(new DeleteEventArgs(asArray), this)) + if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(asArray), this)) return; using (new WriteLock(Locker)) @@ -165,7 +165,7 @@ namespace Umbraco.Core.Services uow.Commit(); - DeletedMemberType.RaiseEvent(new DeleteEventArgs(asArray, false), this); + Deleted.RaiseEvent(new DeleteEventArgs(asArray, false), this); } } } @@ -195,21 +195,21 @@ namespace Umbraco.Core.Services /// /// Occurs before Save /// - public static event TypedEventHandler> SavingMemberType; + public static event TypedEventHandler> Saving; /// /// Occurs after Save /// - public static event TypedEventHandler> SavedMemberType; + public static event TypedEventHandler> Saved; /// /// Occurs before Delete /// - public static event TypedEventHandler> DeletingMemberType; + public static event TypedEventHandler> Deleting; /// /// Occurs after Delete /// - public static event TypedEventHandler> DeletedMemberType; + public static event TypedEventHandler> Deleted; } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index 7b4a3317bb..d685cd7683 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -29,6 +29,7 @@ namespace Umbraco.Core.Services private Lazy _sectionService; private Lazy _macroService; private Lazy _memberTypeService; + private Lazy _memberGroupService; private Lazy _notificationService; /// @@ -46,6 +47,7 @@ namespace Umbraco.Core.Services /// /// /// + /// public ServiceContext( IContentService contentService, IMediaService mediaService, @@ -56,6 +58,7 @@ namespace Umbraco.Core.Services IPackagingService packagingService, IEntityService entityService, IRelationService relationService, + IMemberGroupService memberGroupService/*, ISectionService sectionService, IApplicationTreeService treeService, ITagService tagService) @@ -71,6 +74,7 @@ namespace Umbraco.Core.Services _entityService = new Lazy(() => entityService); _relationService = new Lazy(() => relationService); _sectionService = new Lazy(() => sectionService); + _memberGroupService = new Lazy(() => memberGroupService); _treeService = new Lazy(() => treeService); } @@ -112,7 +116,7 @@ namespace Umbraco.Core.Services _userService = new Lazy(() => new UserService(provider, repositoryFactory.Value)); if (_memberService == null) - _memberService = new Lazy(() => new MemberService(provider, repositoryFactory.Value)); + _memberService = new Lazy(() => new MemberService(provider, repositoryFactory.Value, _memberGroupService.Value)); if (_contentService == null) _contentService = new Lazy(() => new ContentService(provider, repositoryFactory.Value, publishingStrategy)); @@ -155,6 +159,9 @@ namespace Umbraco.Core.Services if (_tagService == null) _tagService = new Lazy(() => new TagService(provider, repositoryFactory.Value)); + if (_memberGroupService == null) + _memberGroupService = new Lazy(() => new MemberGroupService(provider, repositoryFactory.Value)); + } /// @@ -264,7 +271,7 @@ namespace Umbraco.Core.Services /// /// Gets the /// - internal IUserService UserService + public IUserService UserService { get { return _userService.Value; } } @@ -272,7 +279,7 @@ namespace Umbraco.Core.Services /// /// Gets the /// - internal IMemberService MemberService + public IMemberService MemberService { get { return _memberService.Value; } } @@ -296,10 +303,18 @@ namespace Umbraco.Core.Services /// /// Gets the MemberTypeService /// - internal IMemberTypeService MemberTypeService + public IMemberTypeService MemberTypeService { get { return _memberTypeService.Value; } } + + /// + /// Gets the MemberGroupService + /// + public IMemberGroupService MemberGroupService + { + get { return _memberGroupService.Value; } + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index efa4812055..1c59e33d6a 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Web.Security; using Umbraco.Core.Events; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; @@ -18,7 +17,7 @@ namespace Umbraco.Core.Services /// /// Represents the UserService, which is an easy access to operations involving , and eventually Backoffice Users. /// - internal class UserService : IUserService + public class UserService : IUserService { private readonly RepositoryFactory _repositoryFactory; private readonly IDatabaseUnitOfWorkProvider _uowProvider; @@ -78,10 +77,12 @@ namespace Umbraco.Core.Services } } - public IUser CreateMember(string username, string email, string password, IUserType userType) + public IUser CreateMemberWithIdentity(string username, string email, string password, IUserType userType, bool raiseEvents = true) { if (userType == null) throw new ArgumentNullException("userType"); + //TODO: PUT lock here!! + var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateUserRepository(uow)) { @@ -104,14 +105,23 @@ namespace Umbraco.Core.Services IsApproved = true }; + if (raiseEvents) + { + if (SavingUser.IsRaisedEventCancelled(new SaveEventArgs(user), this)) + return user; + } + repository.AddOrUpdate(user); uow.Commit(); + if (raiseEvents) + SavedUser.RaiseEvent(new SaveEventArgs(user, false), this); + return user; } } - public IUser CreateMember(string username, string email, string password, string memberTypeAlias) + public IUser CreateMemberWithIdentity(string username, string email, string password, string memberTypeAlias, bool raiseEvents = true) { var userType = GetUserTypeByAlias(memberTypeAlias); if (userType == null) @@ -119,22 +129,17 @@ namespace Umbraco.Core.Services throw new ArgumentException("The user type " + memberTypeAlias + " could not be resolved"); } - return CreateMember(username, email, password, userType); + return CreateMemberWithIdentity(username, email, password, userType); } - public IUser GetById(object id) + public IUser GetById(int id) { - if (id is int) + using (var repository = _repositoryFactory.CreateUserRepository(_uowProvider.GetUnitOfWork())) { - using (var repository = _repositoryFactory.CreateUserRepository(_uowProvider.GetUnitOfWork())) - { - var user = repository.Get((int) id); + var user = repository.Get((int)id); - return user; - } + return user; } - - return null; } public IUser GetByEmail(string email) @@ -157,26 +162,58 @@ namespace Umbraco.Core.Services } } + /// + /// This disables and renames the user, it does not delete them, use the overload to delete them + /// + /// public void Delete(IUser membershipUser) - { - if (UserDeleting.IsRaisedEventCancelled(new DeleteEventArgs(membershipUser), this)) - return; - - var uow = _uowProvider.GetUnitOfWork(); - using (var repository = _repositoryFactory.CreateUserRepository(uow)) + { + //disable + membershipUser.IsApproved = false; + //can't rename if it's going to take up too many chars + if (membershipUser.Username.Length + 9 <= 125) { - repository.Delete(membershipUser); - uow.Commit(); + membershipUser.Username = DateTime.Now.ToString("yyyyMMdd") + "_" + membershipUser.Username; } + Save(membershipUser); - UserDeleted.RaiseEvent(new DeleteEventArgs(membershipUser, false), this); + //clear out the user logins! + var uow = _uowProvider.GetUnitOfWork(); + uow.Database.Execute("delete from umbracoUserLogins where userID = @id", new {id = membershipUser.Id}); + } + + /// + /// To permanently delete the user pass in true, otherwise they will just be disabled + /// + /// + /// + public void Delete(IUser user, bool deletePermanently) + { + if (deletePermanently == false) + { + Delete(user); + } + else + { + if (DeletingUser.IsRaisedEventCancelled(new DeleteEventArgs(user), this)) + return; + + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateUserRepository(uow)) + { + repository.Delete(user); + uow.Commit(); + } + + DeletedUser.RaiseEvent(new DeleteEventArgs(user, false), this); + } } public void Save(IUser membershipUser, bool raiseEvents = true) { if (raiseEvents) { - if (UserSaving.IsRaisedEventCancelled(new SaveEventArgs(membershipUser), this)) + if (SavingUser.IsRaisedEventCancelled(new SaveEventArgs(membershipUser), this)) return; } @@ -188,14 +225,14 @@ namespace Umbraco.Core.Services } if (raiseEvents) - UserSaved.RaiseEvent(new SaveEventArgs(membershipUser, false), this); + SavedUser.RaiseEvent(new SaveEventArgs(membershipUser, false), this); } public void Save(IEnumerable members, bool raiseEvents = true) { if (raiseEvents) { - if (UserSaving.IsRaisedEventCancelled(new SaveEventArgs(members), this)) + if (SavingUser.IsRaisedEventCancelled(new SaveEventArgs(members), this)) return; } @@ -211,7 +248,7 @@ namespace Umbraco.Core.Services } if (raiseEvents) - UserSaved.RaiseEvent(new SaveEventArgs(members, false), this); + SavedUser.RaiseEvent(new SaveEventArgs(members, false), this); } public IEnumerable FindMembersByEmail(string emailStringToMatch, int pageIndex, int pageSize, out int totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith) @@ -335,15 +372,15 @@ namespace Umbraco.Core.Services public IProfile GetProfileById(int id) { var user = GetUserById(id); - return user; + return user.ProfileData; } public IProfile GetProfileByUserName(string login) { var user = GetByUsername(login); - return user; + return user.ProfileData; } - + public IUser GetUserById(int id) { using (var repository = _repositoryFactory.CreateUserRepository(_uowProvider.GetUnitOfWork())) @@ -352,6 +389,15 @@ namespace Umbraco.Core.Services } } + public IEnumerable GetAllUserTypes(params int[] ids) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateUserTypeRepository(uow)) + { + return repository.GetAll(ids); + } + } + /// /// Gets an IUserType by its Alias /// @@ -367,6 +413,14 @@ namespace Umbraco.Core.Services } } + public IUserType GetUserTypeById(int id) + { + using (var repository = _repositoryFactory.CreateUserTypeRepository(_uowProvider.GetUnitOfWork())) + { + return repository.Get(id); + } + } + /// /// Gets an IUserType by its Name /// @@ -386,7 +440,7 @@ namespace Umbraco.Core.Services { if (raiseEvents) { - if (UserTypeSaving.IsRaisedEventCancelled(new SaveEventArgs(userType), this)) + if (SavingUserType.IsRaisedEventCancelled(new SaveEventArgs(userType), this)) return; } @@ -398,12 +452,12 @@ namespace Umbraco.Core.Services } if (raiseEvents) - UserTypeSaved.RaiseEvent(new SaveEventArgs(userType, false), this); + SavedUserType.RaiseEvent(new SaveEventArgs(userType, false), this); } public void DeleteUserType(IUserType userType) { - if (UserTypeDeleting.IsRaisedEventCancelled(new DeleteEventArgs(userType), this)) + if (DeletingUserType.IsRaisedEventCancelled(new DeleteEventArgs(userType), this)) return; var uow = _uowProvider.GetUnitOfWork(); @@ -413,7 +467,7 @@ namespace Umbraco.Core.Services uow.Commit(); } - UserTypeDeleted.RaiseEvent(new DeleteEventArgs(userType, false), this); + DeletedUserType.RaiseEvent(new DeleteEventArgs(userType, false), this); } /// @@ -436,22 +490,6 @@ namespace Umbraco.Core.Services } } - /// - /// Returns the user's applications that they are allowed to access - /// - /// - /// - public IEnumerable GetUserSections(IUser user) - { - //TODO: We need to cache this result - - var uow = _uowProvider.GetUnitOfWork(); - var sql = new Sql(); - sql.Select("app").From() - .Where(dto => dto.UserId == (int)user.Id); - return uow.Database.Fetch(sql); - } - /// /// Returns permissions for a given user for any number of nodes /// @@ -489,41 +527,41 @@ namespace Umbraco.Core.Services /// /// Occurs before Save /// - public static event TypedEventHandler> UserSaving; + public static event TypedEventHandler> SavingUser; /// /// Occurs after Save /// - public static event TypedEventHandler> UserSaved; + public static event TypedEventHandler> SavedUser; /// /// Occurs before Delete /// - public static event TypedEventHandler> UserDeleting; + public static event TypedEventHandler> DeletingUser; /// /// Occurs after Delete /// - public static event TypedEventHandler> UserDeleted; + public static event TypedEventHandler> DeletedUser; /// /// Occurs before Save /// - public static event TypedEventHandler> UserTypeSaving; + public static event TypedEventHandler> SavingUserType; /// /// Occurs after Save /// - public static event TypedEventHandler> UserTypeSaved; + public static event TypedEventHandler> SavedUserType; /// /// Occurs before Delete /// - public static event TypedEventHandler> UserTypeDeleting; + public static event TypedEventHandler> DeletingUserType; /// /// Occurs after Delete /// - public static event TypedEventHandler> UserTypeDeleted; + public static event TypedEventHandler> DeletedUserType; } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/UserServiceExtensions.cs b/src/Umbraco.Core/Services/UserServiceExtensions.cs new file mode 100644 index 0000000000..b97a40ace8 --- /dev/null +++ b/src/Umbraco.Core/Services/UserServiceExtensions.cs @@ -0,0 +1,52 @@ +using System; +using System.Web.Security; +using Umbraco.Core.Models.Membership; + +namespace Umbraco.Core.Services +{ + internal static class UserServiceExtensions + { + /// + /// Maps a custom provider's information to an umbraco user account + /// + /// + /// + /// + /// To maintain compatibility we have to check the login name if the provider key lookup fails but otherwise + /// we'll store the provider user key in the login column. + /// + public static IUser CreateUserMappingForCustomProvider(this IUserService userService, MembershipUser member) + { + if (member == null) throw new ArgumentNullException("member"); + + + var valToLookup = member.ProviderUserKey == null ? member.UserName : member.ProviderUserKey.ToString(); + var found = userService.GetByUsername(valToLookup); + if (found == null && member.ProviderUserKey != null) + { + //try by username + found = userService.GetByUsername(member.UserName); + } + + if (found == null) + { + var writer = userService.GetUserTypeByAlias("writer"); + if (writer == null) + { + throw new InvalidOperationException("Could not map the custom user to an Umbraco user, no 'writer' user type could be found"); + } + var user = new User( + member.UserName, + member.Email ?? Guid.NewGuid().ToString("N") + "@example.com", //email cannot be empty + member.ProviderUserKey == null ? member.UserName : member.ProviderUserKey.ToString(), + Guid.NewGuid().ToString("N"), //pass cannot be empty + writer); + user.AddAllowedSection(Constants.Applications.Content); + userService.Save(user); + return user; + } + + return found; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 22e43c4e02..21385ac39c 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -337,9 +337,12 @@ + + + @@ -351,6 +354,8 @@ + + @@ -358,6 +363,7 @@ + @@ -963,6 +969,7 @@ + @@ -995,8 +1002,10 @@ + + @@ -1010,6 +1019,7 @@ + @@ -1020,6 +1030,7 @@ + diff --git a/src/Umbraco.Tests/Membership/UmbracoServiceMembershipProviderTests.cs b/src/Umbraco.Tests/Membership/UmbracoServiceMembershipProviderTests.cs index 35092f6bb6..b39bdeeab2 100644 --- a/src/Umbraco.Tests/Membership/UmbracoServiceMembershipProviderTests.cs +++ b/src/Umbraco.Tests/Membership/UmbracoServiceMembershipProviderTests.cs @@ -94,8 +94,8 @@ namespace Umbraco.Tests.Membership mServiceMock.Setup(service => service.GetByEmail("test@test.com")).Returns(() => null); mServiceMock.Setup(service => service.GetDefaultMemberType()).Returns("Member"); mServiceMock.Setup( - service => service.CreateMember(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((string u, string e, string p, string m) => + service => service.CreateMemberWithIdentity(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((string u, string e, string p, string m, bool b) => { createdMember = new Member("test", e, u, p, memberType); }) @@ -125,8 +125,8 @@ namespace Umbraco.Tests.Membership mServiceMock.Setup(service => service.GetByEmail("test@test.com")).Returns(() => null); mServiceMock.Setup(service => service.GetDefaultMemberType()).Returns("Member"); mServiceMock.Setup( - service => service.CreateMember(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((string u, string e, string p, string m) => + service => service.CreateMemberWithIdentity(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((string u, string e, string p, string m, bool b) => { createdMember = new Member("test", e, u, p, memberType); }) @@ -158,8 +158,8 @@ namespace Umbraco.Tests.Membership mServiceMock.Setup(service => service.GetByEmail("test@test.com")).Returns(() => null); mServiceMock.Setup(service => service.GetDefaultMemberType()).Returns("Member"); mServiceMock.Setup( - service => service.CreateMember(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((string u, string e, string p, string m) => + service => service.CreateMemberWithIdentity(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((string u, string e, string p, string m, bool b) => { createdMember = new Member("test", e, u, p, memberType); }) diff --git a/src/Umbraco.Tests/MockTests.cs b/src/Umbraco.Tests/MockTests.cs index 07d6837502..e8201d13ee 100644 --- a/src/Umbraco.Tests/MockTests.cs +++ b/src/Umbraco.Tests/MockTests.cs @@ -49,6 +49,7 @@ namespace Umbraco.Tests new Mock().Object, new RepositoryFactory(true), new Mock().Object), + new Mock().Object/*, new Mock().Object, new Mock().Object, new Mock().Object); @@ -90,6 +91,7 @@ namespace Umbraco.Tests new RepositoryFactory(true), new Mock().Object), new Mock().Object, + new Mock().Object), new Mock().Object, new Mock().Object), CacheHelper.CreateDisabledCacheHelper()); diff --git a/src/Umbraco.Tests/Models/UmbracoEntityTests.cs b/src/Umbraco.Tests/Models/UmbracoEntityTests.cs new file mode 100644 index 0000000000..2be59c7b07 --- /dev/null +++ b/src/Umbraco.Tests/Models/UmbracoEntityTests.cs @@ -0,0 +1,22 @@ +using NUnit.Framework; +using Umbraco.Core.Models; + +namespace Umbraco.Tests.Models +{ + [TestFixture] + public class UmbracoEntityTests + { + [Test] + public void UmbracoEntity_Can_Be_Initialized_From_Dynamic() + { + var boolIsTrue = true; + var intIsTrue = 1; + + var trashedWithBool = new UmbracoEntity((dynamic)boolIsTrue); + var trashedWithInt = new UmbracoEntity((dynamic)intIsTrue); + + Assert.IsTrue(trashedWithBool.Trashed); + Assert.IsTrue(trashedWithInt.Trashed); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs b/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs index 2a58abeacf..bbb5c07731 100644 --- a/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs +++ b/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs @@ -26,8 +26,7 @@ namespace Umbraco.Tests.Persistence string path = TestHelper.CurrentAssemblyDirectory; AppDomain.CurrentDomain.SetData("DataDirectory", path); - RepositoryResolver.Current = new RepositoryResolver( - new RepositoryFactory()); + RepositoryResolver.Current = new RepositoryResolver(new RepositoryFactory(true)); //disable cache var cacheHelper = CacheHelper.CreateDisabledCacheHelper(); diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs index eb0dd2b1ee..9655ea445f 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; @@ -39,6 +40,7 @@ namespace Umbraco.Tests.Persistence.Repositories var templateRepository = new TemplateRepository(unitOfWork, NullCacheProvider.Current); var tagRepository = new TagsRepository(unitOfWork, NullCacheProvider.Current); contentTypeRepository = new ContentTypeRepository(unitOfWork, NullCacheProvider.Current, templateRepository); + var repository = new ContentRepository(unitOfWork, NullCacheProvider.Current, contentTypeRepository, templateRepository, CacheHelper.CreateDisabledCacheHelper()); var repository = new ContentRepository(unitOfWork, NullCacheProvider.Current, contentTypeRepository, templateRepository, tagRepository); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs index f25bcc4959..3104cd5f7f 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs @@ -38,6 +38,7 @@ namespace Umbraco.Tests.Persistence.Repositories var tagRepository = new TagsRepository(unitOfWork, NullCacheProvider.Current); contentTypeRepository = new ContentTypeRepository(unitOfWork, NullCacheProvider.Current, templateRepository); var repository = new ContentRepository(unitOfWork, NullCacheProvider.Current, contentTypeRepository, templateRepository, tagRepository); + var repository = new ContentRepository(unitOfWork, NullCacheProvider.Current, contentTypeRepository, templateRepository, CacheHelper.CreateDisabledCacheHelper()); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs index 23d62e5f8c..e96a3e4790 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs @@ -29,11 +29,13 @@ namespace Umbraco.Tests.Persistence.Repositories base.TearDown(); } - private MemberRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out MemberTypeRepository memberTypeRepository) + private MemberRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out MemberTypeRepository memberTypeRepository, out MemberGroupRepository memberGroupRepository) { memberTypeRepository = new MemberTypeRepository(unitOfWork, NullCacheProvider.Current); var tagRepo = new TagsRepository(unitOfWork, NullCacheProvider.Current); var repository = new MemberRepository(unitOfWork, NullCacheProvider.Current, memberTypeRepository, tagRepo); + memberGroupRepository = new MemberGroupRepository(unitOfWork, NullCacheProvider.Current, CacheHelper.CreateDisabledCacheHelper()); + var repository = new MemberRepository(unitOfWork, NullCacheProvider.Current, memberTypeRepository, memberGroupRepository); return repository; } @@ -57,7 +59,8 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); MemberTypeRepository memberTypeRepository; - using (var repository = CreateRepository(unitOfWork, out memberTypeRepository)) + MemberGroupRepository memberGroupRepository; + using (var repository = CreateRepository(unitOfWork, out memberTypeRepository, out memberGroupRepository)) { var member = CreateTestMember(); @@ -74,7 +77,8 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); MemberTypeRepository memberTypeRepository; - using (var repository = CreateRepository(unitOfWork, out memberTypeRepository)) + MemberGroupRepository memberGroupRepository; + using (var repository = CreateRepository(unitOfWork, out memberTypeRepository, out memberGroupRepository)) { var type = CreateTestMemberType(); var m1 = CreateTestMember(type, "Test 1", "test1@test.com", "pass1", "test1"); @@ -95,7 +99,8 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); MemberTypeRepository memberTypeRepository; - using (var repository = CreateRepository(unitOfWork, out memberTypeRepository)) + MemberGroupRepository memberGroupRepository; + using (var repository = CreateRepository(unitOfWork, out memberTypeRepository, out memberGroupRepository)) { var type = CreateTestMemberType(); for (var i = 0; i < 5; i++) @@ -119,7 +124,8 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); MemberTypeRepository memberTypeRepository; - using (var repository = CreateRepository(unitOfWork, out memberTypeRepository)) + MemberGroupRepository memberGroupRepository; + using (var repository = CreateRepository(unitOfWork, out memberTypeRepository, out memberGroupRepository)) { var key = Guid.NewGuid(); var member = CreateTestMember(key: key); @@ -141,7 +147,8 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); MemberTypeRepository memberTypeRepository; - using (var repository = CreateRepository(unitOfWork, out memberTypeRepository)) + MemberGroupRepository memberGroupRepository; + using (var repository = CreateRepository(unitOfWork, out memberTypeRepository, out memberGroupRepository)) { // Act @@ -162,7 +169,8 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); MemberTypeRepository memberTypeRepository; - using (var repository = CreateRepository(unitOfWork, out memberTypeRepository)) + MemberGroupRepository memberGroupRepository; + using (var repository = CreateRepository(unitOfWork, out memberTypeRepository, out memberGroupRepository)) { // Act @@ -181,7 +189,8 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); MemberTypeRepository memberTypeRepository; - using (var repository = CreateRepository(unitOfWork, out memberTypeRepository)) + MemberGroupRepository memberGroupRepository; + using (var repository = CreateRepository(unitOfWork, out memberTypeRepository, out memberGroupRepository)) { var member = CreateTestMember(); @@ -203,7 +212,8 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); MemberTypeRepository memberTypeRepository; - using (var repository = CreateRepository(unitOfWork, out memberTypeRepository)) + MemberGroupRepository memberGroupRepository; + using (var repository = CreateRepository(unitOfWork, out memberTypeRepository, out memberGroupRepository)) { var memberType = MockedContentTypes.CreateSimpleMemberType(); memberTypeRepository.AddOrUpdate(memberType); @@ -229,7 +239,8 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); MemberTypeRepository memberTypeRepository; - using (var repository = CreateRepository(unitOfWork, out memberTypeRepository)) + MemberGroupRepository memberGroupRepository; + using (var repository = CreateRepository(unitOfWork, out memberTypeRepository, out memberGroupRepository)) { var memberType = MockedContentTypes.CreateSimpleMemberType(); memberTypeRepository.AddOrUpdate(memberType); @@ -257,7 +268,8 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); MemberTypeRepository memberTypeRepository; - using (var repository = CreateRepository(unitOfWork, out memberTypeRepository)) + MemberGroupRepository memberGroupRepository; + using (var repository = CreateRepository(unitOfWork, out memberTypeRepository, out memberGroupRepository)) { var memberType = MockedContentTypes.CreateSimpleMemberType(); memberTypeRepository.AddOrUpdate(memberType); @@ -303,7 +315,8 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); MemberTypeRepository memberTypeRepository; - using (var repository = CreateRepository(unitOfWork, out memberTypeRepository)) + MemberGroupRepository memberGroupRepository; + using (var repository = CreateRepository(unitOfWork, out memberTypeRepository, out memberGroupRepository)) { if (memberType == null) { @@ -325,7 +338,8 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = new PetaPocoUnitOfWorkProvider(); var unitOfWork = provider.GetUnitOfWork(); MemberTypeRepository memberTypeRepository; - using (var repository = CreateRepository(unitOfWork, out memberTypeRepository)) + MemberGroupRepository memberGroupRepository; + using (var repository = CreateRepository(unitOfWork, out memberTypeRepository, out memberGroupRepository)) { var memberType = MockedContentTypes.CreateSimpleMemberType(); memberTypeRepository.AddOrUpdate(memberType); diff --git a/src/Umbraco.Tests/Persistence/Repositories/NotificationsRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/NotificationsRepositoryTest.cs index 4629288f52..36797c1cb3 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/NotificationsRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/NotificationsRepositoryTest.cs @@ -26,7 +26,7 @@ namespace Umbraco.Tests.Persistence.Repositories var node = new NodeDto {CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1,123", SortOrder = 1, Text = "hello", Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0}; var result = unitOfWork.Database.Insert(node); var entity = Mock.Of(e => e.Id == node.NodeId); - var user = Mock.Of(e => e.Id == (object)node.UserId); + var user = Mock.Of(e => e.Id == node.UserId); var notification = repo.CreateNotification(user, entity, "A"); @@ -46,8 +46,8 @@ namespace Umbraco.Tests.Persistence.Repositories var userDto = new UserDto { ContentStartId = -1, Email = "test" , Login = "test" , MediaStartId = -1, Password = "test" , Type = 1, UserName = "test" , UserLanguage = "en" }; unitOfWork.Database.Insert(userDto); - var userNew = Mock.Of(e => e.Id == (object)userDto.Id); - var userAdmin = Mock.Of(e => e.Id == (object)0); + var userNew = Mock.Of(e => e.Id == userDto.Id); + var userAdmin = Mock.Of(e => e.Id == 0); for (var i = 0; i < 10; i++) { @@ -80,7 +80,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var userDto = new UserDto { ContentStartId = -1, Email = "test" + i, Login = "test" + i, MediaStartId = -1, Password = "test", Type = 1, UserName = "test" + i, UserLanguage = "en" }; unitOfWork.Database.Insert(userDto); - var userNew = Mock.Of(e => e.Id == (object)userDto.Id); + var userNew = Mock.Of(e => e.Id == userDto.Id); var notification = repo.CreateNotification(userNew, (i % 2 == 0) ? entity1 : entity2, i.ToString(CultureInfo.InvariantCulture)); } @@ -107,7 +107,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var userDto = new UserDto { ContentStartId = -1, Email = "test" + i, Login = "test" + i, MediaStartId = -1, Password = "test", Type = 1, UserName = "test" + i, UserLanguage = "en" }; unitOfWork.Database.Insert(userDto); - var userNew = Mock.Of(e => e.Id == (object)userDto.Id); + var userNew = Mock.Of(e => e.Id == userDto.Id); var notification = repo.CreateNotification(userNew, (i % 2 == 0) ? entity1 : entity2, i.ToString(CultureInfo.InvariantCulture)); } @@ -126,8 +126,8 @@ namespace Umbraco.Tests.Persistence.Repositories var userDto = new UserDto { ContentStartId = -1, Email = "test", Login = "test", MediaStartId = -1, Password = "test", Type = 1, UserName = "test", UserLanguage = "en" }; unitOfWork.Database.Insert(userDto); - var userNew = Mock.Of(e => e.Id == (object)userDto.Id); - var userAdmin = Mock.Of(e => e.Id == (object)0); + var userNew = Mock.Of(e => e.Id == userDto.Id); + var userAdmin = Mock.Of(e => e.Id == 0); for (var i = 0; i < 10; i++) { diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs index 96b92a612b..7eef71ad63 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using Umbraco.Core; using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Caching; @@ -31,7 +32,7 @@ namespace Umbraco.Tests.Persistence.Repositories private UserRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out UserTypeRepository userTypeRepository) { userTypeRepository = new UserTypeRepository(unitOfWork, NullCacheProvider.Current); - var repository = new UserRepository(unitOfWork, NullCacheProvider.Current, userTypeRepository); + var repository = new UserRepository(unitOfWork, NullCacheProvider.Current, userTypeRepository, CacheHelper.CreateDisabledCacheHelper()); return repository; } diff --git a/src/Umbraco.Tests/Services/MemberServiceTests.cs b/src/Umbraco.Tests/Services/MemberServiceTests.cs index e61d41d4a7..94521a7341 100644 --- a/src/Umbraco.Tests/Services/MemberServiceTests.cs +++ b/src/Umbraco.Tests/Services/MemberServiceTests.cs @@ -3,6 +3,7 @@ using System.Linq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Services; @@ -25,6 +26,264 @@ namespace Umbraco.Tests.Services base.TearDown(); } + [Test] + public void Can_Create_Role() + { + ServiceContext.MemberService.AddRole("MyTestRole"); + + var found = ServiceContext.MemberService.GetAllRoles(); + + Assert.AreEqual(1, found.Count()); + Assert.AreEqual("MyTestRole", found.Single()); + } + + [Test] + public void Can_Create_Duplicate_Role() + { + ServiceContext.MemberService.AddRole("MyTestRole"); + ServiceContext.MemberService.AddRole("MyTestRole"); + + var found = ServiceContext.MemberService.GetAllRoles(); + + Assert.AreEqual(1, found.Count()); + Assert.AreEqual("MyTestRole", found.Single()); + } + + [Test] + public void Can_Get_All_Roles() + { + ServiceContext.MemberService.AddRole("MyTestRole1"); + ServiceContext.MemberService.AddRole("MyTestRole2"); + ServiceContext.MemberService.AddRole("MyTestRole3"); + + var found = ServiceContext.MemberService.GetAllRoles(); + + Assert.AreEqual(3, found.Count()); + } + + [Test] + public void Can_Get_All_Roles_By_Member_Id() + { + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + ServiceContext.MemberTypeService.Save(memberType); + IMember member = MockedMember.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test"); + ServiceContext.MemberService.Save(member); + + ServiceContext.MemberService.AddRole("MyTestRole1"); + ServiceContext.MemberService.AddRole("MyTestRole2"); + ServiceContext.MemberService.AddRole("MyTestRole3"); + ServiceContext.MemberService.AssignRoles(new[] { member.Id }, new[] { "MyTestRole1", "MyTestRole2" }); + + var memberRoles = ServiceContext.MemberService.GetAllRoles(member.Id); + + Assert.AreEqual(2, memberRoles.Count()); + + } + + [Test] + public void Can_Get_All_Roles_By_Member_Username() + { + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + ServiceContext.MemberTypeService.Save(memberType); + IMember member = MockedMember.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test"); + ServiceContext.MemberService.Save(member); + + ServiceContext.MemberService.AddRole("MyTestRole1"); + ServiceContext.MemberService.AddRole("MyTestRole2"); + ServiceContext.MemberService.AddRole("MyTestRole3"); + ServiceContext.MemberService.AssignRoles(new[] { member.Id }, new[] { "MyTestRole1", "MyTestRole2" }); + + var memberRoles = ServiceContext.MemberService.GetAllRoles("test"); + + Assert.AreEqual(2, memberRoles.Count()); + } + + [Test] + public void Can_Delete_Role() + { + ServiceContext.MemberService.AddRole("MyTestRole1"); + + ServiceContext.MemberService.DeleteRole("MyTestRole1", false); + + var memberRoles = ServiceContext.MemberService.GetAllRoles(); + + Assert.AreEqual(0, memberRoles.Count()); + } + + [Test] + public void Throws_When_Deleting_Assigned_Role() + { + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + ServiceContext.MemberTypeService.Save(memberType); + IMember member = MockedMember.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test"); + ServiceContext.MemberService.Save(member); + + ServiceContext.MemberService.AddRole("MyTestRole1"); + ServiceContext.MemberService.AssignRoles(new[] { member.Id }, new[] { "MyTestRole1", "MyTestRole2" }); + + Assert.Throws(() => ServiceContext.MemberService.DeleteRole("MyTestRole1", true)); + } + + [Test] + public void Can_Get_Members_In_Role() + { + ServiceContext.MemberService.AddRole("MyTestRole1"); + var roleId = DatabaseContext.Database.ExecuteScalar("SELECT id from umbracoNode where [text] = 'MyTestRole1'"); + + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + ServiceContext.MemberTypeService.Save(memberType); + var member1 = MockedMember.CreateSimpleMember(memberType, "test1", "test1@test.com", "pass", "test1"); + ServiceContext.MemberService.Save(member1); + var member2 = MockedMember.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2"); + ServiceContext.MemberService.Save(member2); + + DatabaseContext.Database.Insert(new Member2MemberGroupDto {MemberGroup = roleId, Member = member1.Id}); + DatabaseContext.Database.Insert(new Member2MemberGroupDto { MemberGroup = roleId, Member = member2.Id }); + + var membersInRole = ServiceContext.MemberService.GetMembersInRole("MyTestRole1"); + Assert.AreEqual(2, membersInRole.Count()); + } + + [TestCase("MyTestRole1", "test1", StringPropertyMatchType.StartsWith, 1)] + [TestCase("MyTestRole1", "test", StringPropertyMatchType.StartsWith, 3)] + [TestCase("MyTestRole1", "test1", StringPropertyMatchType.Exact, 1)] + [TestCase("MyTestRole1", "test", StringPropertyMatchType.Exact, 0)] + [TestCase("MyTestRole1", "st2", StringPropertyMatchType.EndsWith, 1)] + [TestCase("MyTestRole1", "test%", StringPropertyMatchType.Wildcard, 3)] + public void Find_Members_In_Role(string roleName1, string usernameToMatch, StringPropertyMatchType matchType, int resultCount) + { + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + ServiceContext.MemberTypeService.Save(memberType); + var member1 = MockedMember.CreateSimpleMember(memberType, "test1", "test1@test.com", "pass", "test1"); + ServiceContext.MemberService.Save(member1); + var member2 = MockedMember.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2"); + ServiceContext.MemberService.Save(member2); + var member3 = MockedMember.CreateSimpleMember(memberType, "test3", "test3@test.com", "pass", "test3"); + ServiceContext.MemberService.Save(member3); + + ServiceContext.MemberService.AssignRoles(new[] { member1.Id, member2.Id, member3.Id }, new[] { roleName1 }); + + var result = ServiceContext.MemberService.FindMembersInRole(roleName1, usernameToMatch, matchType); + Assert.AreEqual(resultCount, result.Count()); + } + + [Test] + public void Associate_Members_To_Roles_With_Member_Id() + { + ServiceContext.MemberService.AddRole("MyTestRole1"); + + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + ServiceContext.MemberTypeService.Save(memberType); + var member1 = MockedMember.CreateSimpleMember(memberType, "test1", "test1@test.com", "pass", "test1"); + ServiceContext.MemberService.Save(member1); + var member2 = MockedMember.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2"); + ServiceContext.MemberService.Save(member2); + + ServiceContext.MemberService.AssignRoles(new[] { member1.Id, member2.Id }, new[] { "MyTestRole1" }); + + var membersInRole = ServiceContext.MemberService.GetMembersInRole("MyTestRole1"); + + Assert.AreEqual(2, membersInRole.Count()); + } + + [Test] + public void Associate_Members_To_Roles_With_Member_Username() + { + ServiceContext.MemberService.AddRole("MyTestRole1"); + + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + ServiceContext.MemberTypeService.Save(memberType); + var member1 = MockedMember.CreateSimpleMember(memberType, "test1", "test1@test.com", "pass", "test1"); + ServiceContext.MemberService.Save(member1); + var member2 = MockedMember.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2"); + ServiceContext.MemberService.Save(member2); + + ServiceContext.MemberService.AssignRoles(new[] { member1.Username, member2.Username }, new[] { "MyTestRole1" }); + + var membersInRole = ServiceContext.MemberService.GetMembersInRole("MyTestRole1"); + + Assert.AreEqual(2, membersInRole.Count()); + } + + [Test] + public void Associate_Members_To_Roles_With_Member_Username_Containing_At_Symbols() + { + ServiceContext.MemberService.AddRole("MyTestRole1"); + + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + ServiceContext.MemberTypeService.Save(memberType); + var member1 = MockedMember.CreateSimpleMember(memberType, "test1", "test1@test.com", "pass", "test1@test.com"); + ServiceContext.MemberService.Save(member1); + var member2 = MockedMember.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2@test.com"); + ServiceContext.MemberService.Save(member2); + + ServiceContext.MemberService.AssignRoles(new[] { member1.Username, member2.Username }, new[] { "MyTestRole1" }); + + var membersInRole = ServiceContext.MemberService.GetMembersInRole("MyTestRole1"); + + Assert.AreEqual(2, membersInRole.Count()); + } + + [Test] + public void Associate_Members_To_Roles_With_New_Role() + { + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + ServiceContext.MemberTypeService.Save(memberType); + var member1 = MockedMember.CreateSimpleMember(memberType, "test1", "test1@test.com", "pass", "test1"); + ServiceContext.MemberService.Save(member1); + var member2 = MockedMember.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2"); + ServiceContext.MemberService.Save(member2); + + //implicitly create the role + ServiceContext.MemberService.AssignRoles(new[] { member1.Username, member2.Username }, new[] { "MyTestRole1" }); + + var membersInRole = ServiceContext.MemberService.GetMembersInRole("MyTestRole1"); + + Assert.AreEqual(2, membersInRole.Count()); + } + + [Test] + public void Remove_Members_From_Roles_With_Member_Id() + { + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + ServiceContext.MemberTypeService.Save(memberType); + var member1 = MockedMember.CreateSimpleMember(memberType, "test1", "test1@test.com", "pass", "test1"); + ServiceContext.MemberService.Save(member1); + var member2 = MockedMember.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2"); + ServiceContext.MemberService.Save(member2); + + ServiceContext.MemberService.AssignRoles(new[] { member1.Id, member2.Id }, new[] { "MyTestRole1", "MyTestRole2" }); + + ServiceContext.MemberService.DissociateRoles(new[] {member1.Id }, new[] {"MyTestRole1"}); + ServiceContext.MemberService.DissociateRoles(new[] { member1.Id, member2.Id }, new[] { "MyTestRole2" }); + + var membersInRole = ServiceContext.MemberService.GetMembersInRole("MyTestRole1"); + Assert.AreEqual(1, membersInRole.Count()); + membersInRole = ServiceContext.MemberService.GetMembersInRole("MyTestRole2"); + Assert.AreEqual(0, membersInRole.Count()); + } + + [Test] + public void Remove_Members_From_Roles_With_Member_Username() + { + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + ServiceContext.MemberTypeService.Save(memberType); + var member1 = MockedMember.CreateSimpleMember(memberType, "test1", "test1@test.com", "pass", "test1"); + ServiceContext.MemberService.Save(member1); + var member2 = MockedMember.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2"); + ServiceContext.MemberService.Save(member2); + + ServiceContext.MemberService.AssignRoles(new[] { member1.Username, member2.Username }, new[] { "MyTestRole1", "MyTestRole2" }); + + ServiceContext.MemberService.DissociateRoles(new[] { member1.Username }, new[] { "MyTestRole1" }); + ServiceContext.MemberService.DissociateRoles(new[] { member1.Username, member2.Username }, new[] { "MyTestRole2" }); + + var membersInRole = ServiceContext.MemberService.GetMembersInRole("MyTestRole1"); + Assert.AreEqual(1, membersInRole.Count()); + membersInRole = ServiceContext.MemberService.GetMembersInRole("MyTestRole2"); + Assert.AreEqual(0, membersInRole.Count()); + } + [Test] public void Can_Delete_member() { @@ -108,8 +367,8 @@ namespace Umbraco.Tests.Services IMember member = MockedMember.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test"); ServiceContext.MemberService.Save(member); - Assert.IsNotNull(ServiceContext.MemberService.GetById((object)member.Id)); - Assert.IsNull(ServiceContext.MemberService.GetById((object)9876)); + Assert.IsNotNull(ServiceContext.MemberService.GetById(member.Id)); + Assert.IsNull(ServiceContext.MemberService.GetById(9876)); } [Test] diff --git a/src/Umbraco.Tests/Services/MemberTypeServiceTests.cs b/src/Umbraco.Tests/Services/MemberTypeServiceTests.cs index 3bbb6b7bf6..bf93abe8e7 100644 --- a/src/Umbraco.Tests/Services/MemberTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/MemberTypeServiceTests.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using umbraco.cms.presentation.create.controls; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; @@ -24,6 +25,66 @@ namespace Umbraco.Tests.Services base.TearDown(); } + [Test] + public void Member_Cannot_Edit_Property() + { + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + ServiceContext.MemberTypeService.Save(memberType); + //re-get + memberType = ServiceContext.MemberTypeService.Get(memberType.Id); + foreach (var p in memberType.PropertyTypes) + { + Assert.IsFalse(memberType.MemberCanEditProperty(p.Alias)); + } + } + + [Test] + public void Member_Can_Edit_Property() + { + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + ServiceContext.MemberTypeService.Save(memberType); + var prop = memberType.PropertyTypes.First().Alias; + memberType.SetMemberCanEditProperty(prop, true); + ServiceContext.MemberTypeService.Save(memberType); + //re-get + memberType = ServiceContext.MemberTypeService.Get(memberType.Id); + foreach (var p in memberType.PropertyTypes.Where(x => x.Alias != prop)) + { + Assert.IsFalse(memberType.MemberCanEditProperty(p.Alias)); + } + Assert.IsTrue(memberType.MemberCanEditProperty(prop)); + } + + [Test] + public void Member_Cannot_View_Property() + { + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + ServiceContext.MemberTypeService.Save(memberType); + //re-get + memberType = ServiceContext.MemberTypeService.Get(memberType.Id); + foreach (var p in memberType.PropertyTypes) + { + Assert.IsFalse(memberType.MemberCanViewProperty(p.Alias)); + } + } + + [Test] + public void Member_Can_View_Property() + { + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + ServiceContext.MemberTypeService.Save(memberType); + var prop = memberType.PropertyTypes.First().Alias; + memberType.SetMemberCanViewProperty(prop, true); + ServiceContext.MemberTypeService.Save(memberType); + //re-get + memberType = ServiceContext.MemberTypeService.Get(memberType.Id); + foreach (var p in memberType.PropertyTypes.Where(x => x.Alias != prop)) + { + Assert.IsFalse(memberType.MemberCanViewProperty(p.Alias)); + } + Assert.IsTrue(memberType.MemberCanViewProperty(prop)); + } + [Test] public void Deleting_PropertyType_Removes_The_Property_From_Member() { diff --git a/src/Umbraco.Tests/Services/UserServiceTests.cs b/src/Umbraco.Tests/Services/UserServiceTests.cs index 4b1e5c421a..6aee0ef5f8 100644 --- a/src/Umbraco.Tests/Services/UserServiceTests.cs +++ b/src/Umbraco.Tests/Services/UserServiceTests.cs @@ -9,6 +9,7 @@ using Umbraco.Tests.TestHelpers.Entities; using umbraco.BusinessLogic.Actions; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Services; +using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; using umbraco.BusinessLogic.Actions; @@ -38,7 +39,7 @@ namespace Umbraco.Tests.Services // Arrange var userService = ServiceContext.UserService; var userType = userService.GetUserTypeByAlias("admin"); - var user = ServiceContext.UserService.CreateMember("test1", "test1@test.com", "123456", userType); + var user = ServiceContext.UserService.CreateMemberWithIdentity("test1", "test1@test.com", "123456", userType); var contentType = MockedContentTypes.CreateSimpleContentType(); ServiceContext.ContentTypeService.Save(contentType); var content = new[] @@ -65,7 +66,7 @@ namespace Umbraco.Tests.Services // Arrange var userService = ServiceContext.UserService; var userType = userService.GetUserTypeByAlias("admin"); - var user = ServiceContext.UserService.CreateMember("test1", "test1@test.com", "123456", userType); + var user = ServiceContext.UserService.CreateMemberWithIdentity("test1", "test1@test.com", "123456", userType); var contentType = MockedContentTypes.CreateSimpleContentType(); ServiceContext.ContentTypeService.Save(contentType); var content = new[] @@ -99,21 +100,35 @@ namespace Umbraco.Tests.Services { var userType = MockedUserType.CreateUserType(); ServiceContext.UserService.SaveUserType(userType); - var user = ServiceContext.UserService.CreateMember("JohnDoe", "john@umbraco.io", "12345", userType); + var user = ServiceContext.UserService.CreateMemberWithIdentity("JohnDoe", "john@umbraco.io", "12345", userType); - ServiceContext.UserService.Delete(user); + ServiceContext.UserService.Delete(user, true); var deleted = ServiceContext.UserService.GetById(user.Id); // Assert Assert.That(deleted, Is.Null); } + [Test] + public void Disables_User_Instead_Of_Deleting_If_Flag_Not_Set() + { + var userType = MockedUserType.CreateUserType(); + ServiceContext.UserService.SaveUserType(userType); + var user = ServiceContext.UserService.CreateMemberWithIdentity("JohnDoe", "john@umbraco.io", "12345", userType); + + ServiceContext.UserService.Delete(user); + var deleted = ServiceContext.UserService.GetById(user.Id); + + // Assert + Assert.That(deleted, Is.Not.Null); + } + [Test] public void Exists_By_Username() { var userType = MockedUserType.CreateUserType(); ServiceContext.UserService.SaveUserType(userType); - var user = ServiceContext.UserService.CreateMember("JohnDoe", "john@umbraco.io", "12345", userType); + var user = ServiceContext.UserService.CreateMemberWithIdentity("JohnDoe", "john@umbraco.io", "12345", userType); Assert.IsTrue(ServiceContext.UserService.Exists("JohnDoe")); Assert.IsFalse(ServiceContext.UserService.Exists("notFound")); @@ -124,7 +139,7 @@ namespace Umbraco.Tests.Services { var userType = MockedUserType.CreateUserType(); ServiceContext.UserService.SaveUserType(userType); - var user = ServiceContext.UserService.CreateMember("JohnDoe", "john@umbraco.io", "12345", userType); + var user = ServiceContext.UserService.CreateMemberWithIdentity("JohnDoe", "john@umbraco.io", "12345", userType); Assert.IsNotNull(ServiceContext.UserService.GetByEmail(user.Email)); Assert.IsNull(ServiceContext.UserService.GetByEmail("do@not.find")); @@ -135,7 +150,7 @@ namespace Umbraco.Tests.Services { var userType = MockedUserType.CreateUserType(); ServiceContext.UserService.SaveUserType(userType); - var user = ServiceContext.UserService.CreateMember("JohnDoe", "john@umbraco.io", "12345", userType); + var user = ServiceContext.UserService.CreateMemberWithIdentity("JohnDoe", "john@umbraco.io", "12345", userType); Assert.IsNotNull(ServiceContext.UserService.GetByUsername(user.Username)); Assert.IsNull(ServiceContext.UserService.GetByUsername("notFound")); @@ -146,7 +161,7 @@ namespace Umbraco.Tests.Services { var userType = MockedUserType.CreateUserType(); ServiceContext.UserService.SaveUserType(userType); - var user = ServiceContext.UserService.CreateMember("JohnDoe", "john@umbraco.io", "12345", userType); + var user = ServiceContext.UserService.CreateMemberWithIdentity("JohnDoe", "john@umbraco.io", "12345", userType); Assert.IsNotNull(ServiceContext.UserService.GetById(user.Id)); Assert.IsNull(ServiceContext.UserService.GetById(9876)); @@ -328,10 +343,11 @@ namespace Umbraco.Tests.Services var userType = userService.GetUserTypeByAlias("admin"); // Act - var membershipUser = userService.CreateMember("JohnDoe", "john@umbraco.io", "12345", userType); + var membershipUser = userService.CreateMemberWithIdentity("JohnDoe", "john@umbraco.io", "12345", userType); // Assert Assert.That(membershipUser.HasIdentity, Is.True); + Assert.That(membershipUser.Id, Is.GreaterThan(0)); IUser user = membershipUser as User; Assert.That(user, Is.Not.Null); Assert.That(user.DefaultPermissions, Is.EqualTo(userType.Permissions)); @@ -350,7 +366,7 @@ namespace Umbraco.Tests.Services var hash = new HMACSHA1(); hash.Key = Encoding.Unicode.GetBytes(password); var encodedPassword = Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password))); - var membershipUser = userService.CreateMember("JohnDoe", "john@umbraco.io", encodedPassword, userType); + var membershipUser = userService.CreateMemberWithIdentity("JohnDoe", "john@umbraco.io", encodedPassword, userType); // Assert Assert.That(membershipUser.HasIdentity, Is.True); @@ -366,8 +382,8 @@ namespace Umbraco.Tests.Services { var userType = ServiceContext.UserService.GetUserTypeByAlias("admin"); - var user1 = ServiceContext.UserService.CreateMember("test1", "test1@test.com", "test1", userType); - var user2 = ServiceContext.UserService.CreateMember("test2", "test2@test.com", "test2", userType); + var user1 = ServiceContext.UserService.CreateMemberWithIdentity("test1", "test1@test.com", "test1", userType); + var user2 = ServiceContext.UserService.CreateMemberWithIdentity("test2", "test2@test.com", "test2", userType); //adds some allowed sections user1.AddAllowedSection("test"); @@ -391,7 +407,7 @@ namespace Umbraco.Tests.Services { // Arrange var userType = ServiceContext.UserService.GetUserTypeByAlias("admin"); - var user = ServiceContext.UserService.CreateMember("test1", "test1@test.com", "test1", userType); + var user = ServiceContext.UserService.CreateMemberWithIdentity("test1", "test1@test.com", "test1", userType); // Act @@ -408,7 +424,7 @@ namespace Umbraco.Tests.Services { // Arrange var userType = ServiceContext.UserService.GetUserTypeByAlias("admin"); - var user = (IUser)ServiceContext.UserService.CreateMember("test1", "test1@test.com", "test1", userType); + var user = (IUser)ServiceContext.UserService.CreateMemberWithIdentity("test1", "test1@test.com", "test1", userType); // Act @@ -425,7 +441,7 @@ namespace Umbraco.Tests.Services { // Arrange var userType = ServiceContext.UserService.GetUserTypeByAlias("admin"); - var originalUser = (User)ServiceContext.UserService.CreateMember("test1", "test1@test.com", "test1", userType); + var originalUser = (User)ServiceContext.UserService.CreateMemberWithIdentity("test1", "test1@test.com", "test1", userType); // Act diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index 46caba40c5..bf67b5610c 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -80,6 +80,8 @@ namespace Umbraco.Tests.TestHelpers using (DisposableTimer.TraceDuration("init")) { + //TODO: Somehow make this faster - takes 5s + + DatabaseContext.Initialize(dbFactory.ProviderName, dbFactory.ConnectionString); CreateSqlCeDatabase(); InitializeDatabase(); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 955992e644..c84a44df45 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -172,6 +172,7 @@ + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index a61e8011b2..67df44596e 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -2136,7 +2136,6 @@ - @@ -2161,21 +2160,6 @@ - - - - - - - - - - - - - - - @@ -2618,6 +2602,7 @@ + diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml index 735291e38c..33e05dfd36 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml @@ -2,31 +2,40 @@ @using System.Web.Mvc.Html @using ClientDependency.Core.Mvc -@using umbraco.cms.businesslogic.member @using Umbraco.Web @using Umbraco.Web.Models @using Umbraco.Web.Controllers @{ - var profileModel = new ProfileModel(); + var profileModel = Members.CreateProfileModel(); Html.EnableClientValidation(); Html.EnableUnobtrusiveJavaScript(); Html.RequiresJs("/umbraco_client/ui/jquery.js"); Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.min.js"); Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); + + var success = TempData["ProfileUpdateSuccess"] != null; + } -@if (Member.IsLoggedOn()) -{ - @Html.RenderJsHere() +@*NOTE: This RenderJsHere code should be put on your main template page where the rest of your script tags are placed*@ +@Html.RenderJsHere() +@if (Members.IsLoggedIn()) +{ + if (success) + { + // This message will show if RedirectOnSucces is set to false (default) +

Profile updated

+ } + using (Html.BeginUmbracoForm("HandleUpdateProfile")) {
Edit profile - @Html.ValidationSummary(true) + @Html.ValidationSummary("profileModel", true) @Html.LabelFor(m => profileModel.Name) @Html.TextBoxFor(m => profileModel.Name) @@ -45,10 +54,8 @@ @Html.HiddenFor(m => profileModel.MemberProperties[i].Alias)
} - - @Html.HiddenFor(m => profileModel.MemberTypeAlias)
- } + } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml index 73b891d448..846f9a987d 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml @@ -16,6 +16,7 @@ Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); } +@*NOTE: This RenderJsHere code should be put on your main template page where the rest of your script tags are placed*@ @Html.RenderJsHere() @using (Html.BeginUmbracoForm("HandleLogin")) @@ -23,7 +24,7 @@
Login - @Html.ValidationSummary(true) + @Html.ValidationSummary("loginModel", true) @Html.LabelFor(m => loginModel.Username) @Html.TextBoxFor(m => loginModel.Username) diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml index 7da6b8e8a3..bc516d6fa2 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml @@ -1,19 +1,31 @@ @inherits Umbraco.Web.Macros.PartialViewMacroPage +@using System.Web.Mvc.Html @using ClientDependency.Core.Mvc @using Umbraco.Web @using Umbraco.Web.Models @using Umbraco.Web.Controllers @{ - var loginStatusModel = new LoginStatusModel(); + var loginStatusModel = Members.GetCurrentLoginStatus(); Html.EnableClientValidation(); Html.EnableUnobtrusiveJavaScript(); Html.RequiresJs("/umbraco_client/ui/jquery.js"); Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.min.js"); Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); + + var logoutModel = new PostRedirectModel(); + + /* + * Here you can specify a redirect URL for after logging out, by default umbraco will simply + * redirect to the current page. Example to redirect to the home page: + * + * logoutModel.RedirectUrl = "/"; + * + */ } +@*NOTE: This RenderJsHere code should be put on your main template page where the rest of your script tags are placed*@ @Html.RenderJsHere() @if (loginStatusModel.IsLoggedIn) @@ -26,5 +38,7 @@ Logout
+ + @Html.HiddenFor(m => logoutModel.RedirectUrl) } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml index 014b33ac2e..d0abf09e96 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml @@ -7,27 +7,33 @@ @using Umbraco.Web.Controllers @{ - var registerModel = new RegisterModel(); - @* - Configurable here: + /* + * You can specify a custom member type alias in the constructor, the default is 'Member' + * for example, to use 'Custom Member' you'd use this syntax: + * + * var registerModel = Members.CreateRegistrationModel("Custom Member"); - registerModel.MemberTypeAlias - the default is "Member" - - registerModel.RedirectOnSucces - the default is false - - registerModel.RedirectUrl - what page do we go to if the registration was successful? - the default is "/" unless RedirectOnSuccess is set to false - - registerModel.UsernameIsEmail - the default is true - if you want the username to be different from the email - address, set this to true and add a new Username field in - the form below - - @Html.LabelFor(m => registerModel.Username) - @Html.TextBoxFor(m => registerModel.Username) - @Html.ValidationMessageFor(m => registerModel.Username) - *@ + */ + + var registerModel = Members.CreateRegistrationModel(); + + /* + * Configurable here: + * + * registerModel.RedirectUrl - Optional. What path to redirect to if registration is successful. + * By default the member will be redirected to the current umbraco page + * unless this is specified. + * + * registerModel.UsernameIsEmail - the default is true + * if you want the username to be different from the email + * address, set this to true and add a new Username field in + * the form below + * + * @Html.LabelFor(m => registerModel.Username) + * @Html.TextBoxFor(m => registerModel.Username) + * @Html.ValidationMessageFor(m => registerModel.Username) + */ Html.EnableClientValidation(); Html.EnableUnobtrusiveJavaScript(); @@ -38,6 +44,9 @@ var success = TempData["FormSuccess"] != null; } +@*NOTE: This RenderJsHere code should be put on your main template page where the rest of your script tags are placed*@ +@Html.RenderJsHere() + @if (success) { // This message will show if RedirectOnSucces is set to false (default) @@ -49,8 +58,8 @@ else {
Register Member - - @Html.ValidationSummary(true) + + @Html.ValidationSummary("registerModel", true) @Html.LabelFor(m => registerModel.Name) @Html.TextBoxFor(m => registerModel.Name) @@ -67,7 +76,7 @@ else @Html.ValidationMessageFor(m => registerModel.Password)
- @if (registerModel.MemberProperties != null) { + @if (registerModel.MemberProperties != null) { for (var i = 0; i < registerModel.MemberProperties.Count; i++) { @Html.LabelFor(m => registerModel.MemberProperties[i].Value, registerModel.MemberProperties[i].Name) @@ -78,13 +87,10 @@ else } @Html.HiddenFor(m => registerModel.MemberTypeAlias) - @Html.HiddenFor(m => registerModel.RedirectOnSucces) @Html.HiddenFor(m => registerModel.RedirectUrl) @Html.HiddenFor(m => registerModel.UsernameIsEmail) -
- - @Html.RenderJsHere() + } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Breadcrumb.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Breadcrumb.cshtml deleted file mode 100644 index 4c79cfef3d..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Breadcrumb.cshtml +++ /dev/null @@ -1,22 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage -@* - This snippet makes a breadcrumb of parents using an unordred html list. - - How it works: - - It uses the Ancestors() method to get all parents and then generates links so the visitor get go back - - Finally it outputs the name of the current page (without a link) -*@ - -@if (CurrentPage.Ancestors().Any()) -{ - -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EditProfile.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EditProfile.cshtml deleted file mode 100644 index ef7bd8a7eb..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EditProfile.cshtml +++ /dev/null @@ -1,54 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@using System.Web.Mvc.Html -@using ClientDependency.Core.Mvc -@using umbraco.cms.businesslogic.member -@using Umbraco.Web -@using Umbraco.Web.Models -@using Umbraco.Web.Controllers - -@{ - var profileModel = new ProfileModel(); - - Html.EnableClientValidation(); - Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("/umbraco_client/ui/jquery.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.min.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); -} - -@if (Member.IsLoggedOn()) -{ - @Html.RenderJsHere() - - using (Html.BeginUmbracoForm("HandleUpdateProfile")) - { -
- Edit profile - - @Html.ValidationSummary(true) - - @Html.LabelFor(m => profileModel.Name) - @Html.TextBoxFor(m => profileModel.Name) - @Html.ValidationMessageFor(m => profileModel.Name) -
- - @Html.LabelFor(m => profileModel.Email) - @Html.TextBoxFor(m => profileModel.Email) - @Html.ValidationMessageFor(m => profileModel.Email) -
- - @for (var i = 0; i < profileModel.MemberProperties.Count; i++) - { - @Html.LabelFor(m => profileModel.MemberProperties[i].Value, profileModel.MemberProperties[i].Name) - @Html.EditorFor(m => profileModel.MemberProperties[i].Value) - @Html.HiddenFor(m => profileModel.MemberProperties[i].Alias) -
- } - - @Html.HiddenFor(m => profileModel.MemberTypeAlias) - - -
- } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty (ForUseWithCustomViews).cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty (ForUseWithCustomViews).cshtml deleted file mode 100644 index 8d10a3a9c7..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty (ForUseWithCustomViews).cshtml +++ /dev/null @@ -1 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoViewPage \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml deleted file mode 100644 index 2363dcc14c..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml +++ /dev/null @@ -1 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListAncestorsFromCurrentPage.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListAncestorsFromCurrentPage.cshtml deleted file mode 100644 index e1b5d1280b..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListAncestorsFromCurrentPage.cshtml +++ /dev/null @@ -1,16 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@* Check the current page has ancestors *@ -@if (CurrentPage.Ancestors().Any()) -{ -
    - @* For each page in the ancestors collection which have been ordered by Level (so we start with the highest top node first) *@ - @foreach (var page in CurrentPage.Ancestors().OrderBy("Level")) - { -
  • @page.Name »
  • - } - - @* Display the current page as the last item in the list *@ -
  • @CurrentPage.Name
  • -
-} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesFromCurrentPage.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesFromCurrentPage.cshtml deleted file mode 100644 index 91726cd546..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesFromCurrentPage.cshtml +++ /dev/null @@ -1,15 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@* Ensure that the Current Page has children, where the property umbracoNaviHide is not True *@ -@if (CurrentPage.Children.Where("Visible").Any()) -{ -
    - @* For each child page under the root node, where the property umbracoNaviHide is not True *@ - @foreach (var childPage in CurrentPage.Children.Where("Visible")) - { -
  • - @childPage.Name -
  • - } -
-} diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesOrderedByDate.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesOrderedByDate.cshtml deleted file mode 100644 index 344b72247c..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesOrderedByDate.cshtml +++ /dev/null @@ -1,10 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -
    - @*OrderBy() takes the property to sort by and optionally order desc/asc *@ - - @foreach (var page in CurrentPage.Children.Where("Visible").OrderBy("CreateDate desc")) - { -
  • @page.Name
  • - } -
diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesOrderedByName.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesOrderedByName.cshtml deleted file mode 100644 index 191f4dbf3b..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesOrderedByName.cshtml +++ /dev/null @@ -1,8 +0,0 @@ -@inherits Umbraco.Web.Macros.PartialViewMacroPage -
    - @*OrderBy() takes the property to sort by*@ - @foreach (var page in CurrentPage.Children.Where("Visible").OrderBy("Name")) - { -
  • @page.Name
  • - } -
diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesWithDoctype.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesWithDoctype.cshtml deleted file mode 100644 index f44964335e..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListChildPagesWithDoctype.cshtml +++ /dev/null @@ -1,32 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@* - This snippet shows how simple it is to fetch only children of a certain Document Type using Razor. Instead of - calling .Children, simply call .AliasOfDocumentType in plural. - For instance .Textpages or .NewsArticles (you can find the alias of your Document Type by editing it in the - Settings section). -*@ - - -@{ - @*Build a query and return the visible items *@ - var selection= CurrentPage.Textpages.Where("Visible"); - - @* - Example of more querying, if you have a true/false property with the alias of shouldBeFeatured: - var selection= Model.Textpages.Where("shouldBeFeatured == true").Where("Visible"); - *@ -} - - -@*Determine if there are any nodes in the selection, then render list *@ -@if(selection.Any()){ - -
    - @foreach(var page in selection){ -
  • @page.Name
  • - } -
- -} - diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListDescendantsFromCurrentPage.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListDescendantsFromCurrentPage.cshtml deleted file mode 100644 index 40c343688c..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/ListDescendantsFromCurrentPage.cshtml +++ /dev/null @@ -1,54 +0,0 @@ -@inherits Umbraco.Web.Macros.PartialViewMacroPage - -@* Ensure that the Current Page has children, where the property umbracoNaviHide is not True *@ -@if (CurrentPage.Children.Where("Visible").Any()) -{ - @* Get the first page in the children, where the property umbracoNaviHide is not True *@ - var naviLevel = CurrentPage.Children.Where("Visible").First().Level; - - @* Add in level for a CSS hook *@ -
    - @* For each child page under the root node, where the property umbracoNaviHide is not True *@ - @foreach (var childPage in CurrentPage.Children.Where("Visible")) - { -
  • - @childPage.Name - - @* if the current page has any children, where the property umbracoNaviHide is not True *@ - @if (childPage.Children.Where("Visible").Any()) - { - @* Call our helper to display the children *@ - @childPages(childPage.Children) - } -
  • - } -
-} - - -@helper childPages(dynamic pages) - { - @* Ensure that we have a collection of pages *@ - if (pages.Any()) - { - @* Get the first page in pages and get the level *@ - var naviLevel = pages.First().Level; - - @* Add in level for a CSS hook *@ -
    - @foreach (var page in pages.Where("Visible")) - { -
  • - @page.Name - - @* if the current page has any children, where the property umbracoNaviHide is not True *@ - @if (page.Children.Where("Visible").Any()) - { - @* Call our helper to display the children *@ - @childPages(page.Children) - } -
  • - } -
- } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Login.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Login.cshtml deleted file mode 100644 index 9f0a8018b7..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Login.cshtml +++ /dev/null @@ -1,40 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@using System.Web.Mvc.Html -@using ClientDependency.Core.Mvc -@using Umbraco.Web -@using Umbraco.Web.Models -@using Umbraco.Web.Controllers - -@{ - var loginModel = new LoginModel(); - - Html.EnableClientValidation(); - Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("/umbraco_client/ui/jquery.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.min.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); -} - -@Html.RenderJsHere() - -@using (Html.BeginUmbracoForm("HandleLogin")) -{ -
- Login - - @Html.ValidationSummary(true) - - @Html.LabelFor(m => loginModel.Username) - @Html.TextBoxFor(m => loginModel.Username) - @Html.ValidationMessageFor(m => loginModel.Username) -
- - @Html.LabelFor(m => loginModel.Password) - @Html.PasswordFor(m => loginModel.Password) - @Html.ValidationMessageFor(m => loginModel.Password) -
- - -
-} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/LoginStatus.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/LoginStatus.cshtml deleted file mode 100644 index 996c21820c..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/LoginStatus.cshtml +++ /dev/null @@ -1,31 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@using ClientDependency.Core.Mvc -@using Umbraco.Web -@using Umbraco.Web.Models -@using Umbraco.Web.Controllers - -@{ - var loginStatusModel = new LoginStatusModel(); - - Html.EnableClientValidation(); - Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("/umbraco_client/ui/jquery.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.min.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); -} - -@Html.RenderJsHere() - -@if (loginStatusModel.IsLoggedIn) -{ -

You are currently logged in as @loginStatusModel.Name

- - using (Html.BeginUmbracoForm("HandleLogout")) - { -
- Logout - -
- } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/MultinodeTree-picker.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/MultinodeTree-picker.cshtml deleted file mode 100644 index b3e292230d..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/MultinodeTree-picker.cshtml +++ /dev/null @@ -1,20 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@* - Macro to list nodes from a Multinode tree picker, using the pickers default settings. - Content Values stored as xml. - - To get it working with any site's data structure, simply set the selection equal to the property which has the - multinode treepicker (so: replace "PropertyWithPicker" with the alias of your property). -*@ - -@* Lists each selected value from the picker as a link *@ -
    - @foreach(var id in CurrentPage.PropertyWithPicker.Split(',')){ - - @*For each link, get the node, and display its name and url*@ - var content = Umbraco.Content(id); - -
  • @content.Name
  • - } -
\ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Navigation.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Navigation.cshtml deleted file mode 100644 index 626dade1ab..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Navigation.cshtml +++ /dev/null @@ -1,22 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@* - Macro to display child pages below the root page of a standard website. - Also highlights the current active page/section in the navigation with - the css class "current". -*@ - - -@{ - @*Get the root of the website *@ - var root = CurrentPage.AncestorOrSelf(1); -} - -
    - @foreach (var page in root.Children.Where("Visible")) - { -
  • - @page.Name -
  • - } -
diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/RegisterMember.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/RegisterMember.cshtml deleted file mode 100644 index dad5d256d5..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/RegisterMember.cshtml +++ /dev/null @@ -1,90 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@using System.Web.Mvc.Html -@using ClientDependency.Core.Mvc -@using Umbraco.Web -@using Umbraco.Web.Models -@using Umbraco.Web.Controllers - -@{ - var registerModel = new RegisterModel(); - - @* - Configurable here: - - registerModel.MemberTypeAlias - the default is "Member" - - registerModel.RedirectOnSucces - the default is false - - registerModel.RedirectUrl - what page do we go to if the registration was successful? - the default is "/" unless RedirectOnSuccess is set to false - - registerModel.UsernameIsEmail - the default is true - if you want the username to be different from the email - address, set this to true and add a new Username field in - the form below - - @Html.LabelFor(m => registerModel.Username) - @Html.TextBoxFor(m => registerModel.Username) - @Html.ValidationMessageFor(m => registerModel.Username) - *@ - - Html.EnableClientValidation(); - Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("/umbraco_client/ui/jquery.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.min.js"); - Html.RequiresJs("/umbraco_client/Application/JQuery/jquery.validate.unobtrusive.min.js"); - - var success = TempData["FormSuccess"] != null; -} - -@if (success) -{ - // This message will show if RedirectOnSucces is set to false (default) -

Registration succeeeded.

-} -else -{ - using (Html.BeginUmbracoForm("HandleRegisterMember")) - { -
- Register Member - - @Html.ValidationSummary(true) - - @Html.LabelFor(m => registerModel.Name) - @Html.TextBoxFor(m => registerModel.Name) - @Html.ValidationMessageFor(m => registerModel.Name) -
- - @Html.LabelFor(m => registerModel.Email) - @Html.TextBoxFor(m => registerModel.Email) - @Html.ValidationMessageFor(m => registerModel.Email) -
- - @Html.LabelFor(m => registerModel.Password) - @Html.PasswordFor(m => registerModel.Password) - @Html.ValidationMessageFor(m => registerModel.Password) -
- - @if (registerModel.MemberProperties != null) { - for (var i = 0; i < registerModel.MemberProperties.Count; i++) - { - @Html.LabelFor(m => registerModel.MemberProperties[i].Value, registerModel.MemberProperties[i].Name) - @Html.EditorFor(m => registerModel.MemberProperties[i].Value) - @Html.HiddenFor(m => registerModel.MemberProperties[i].Alias) -
- } - } - - @Html.HiddenFor(m => registerModel.MemberTypeAlias) - @Html.HiddenFor(m => registerModel.RedirectOnSucces) - @Html.HiddenFor(m => registerModel.RedirectUrl) - @Html.HiddenFor(m => registerModel.UsernameIsEmail) - - -
- - @Html.RenderJsHere() - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/SiteMap.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/SiteMap.cshtml deleted file mode 100644 index 011cadbd64..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/SiteMap.cshtml +++ /dev/null @@ -1,32 +0,0 @@ -@inherits Umbraco.Web.Mvc.UmbracoTemplatePage - -@*Render the sitemap by passing the root node to the traverse helper*@ -
- @Traverse(CurrentPage.AncestorOrSelf()) -
- -@*Helper method to travers through all descendants*@ -@helper Traverse(dynamic node) -{ - @* Update the level to reflect how deep you want the sitemap to go *@ - var maxLevelForSitemap = 4; - - @*Select visible children *@ - var items = node.Children.Where("Visible").Where("Level <= " + maxLevelForSitemap); - - @*If any items are returned, render a list *@ - if (items.Any()) - { -
    - @foreach (var item in items) - { -
  • - @item.Name - - @*Run the traverse helper again *@ - @Traverse(item) -
  • - } -
- } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs b/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs index 6ba27b5fe7..07a918acc0 100644 --- a/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs +++ b/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.IO; using System.Linq; using System.Web.UI; using System.Web.UI.WebControls; @@ -21,26 +23,66 @@ namespace Umbraco.Web.UI.Umbraco.Create private static void LoadTemplates(ListControl list) { - var path = IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViews/Templates/"); + var partialViewTemplatePath = new + { + path = IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViews/Templates/"), + include = new string[]{} + }; + //include these templates from the partial view macro templates! + var partialViewMacrosTemplatePath = new + { + path = IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViewMacros/Templates/"), + include = new[] + { + "Breadcrumb", + "EditProfile", + "Empty (ForUseWithCustomViews)", + "Empty", + "ListAncestorsFromCurrentPage", + "ListChildPagesFromCurrentPage", + "ListChildPagesOrderedByDate", + "ListChildPagesOrderedByName", + "ListChildPagesWithDoctype", + "ListDescendantsFromCurrentPage", + "Login", + "LoginStatus", + "MultinodeTree-picker", + "Navigation", + "RegisterMember", + "SiteMap" + } + }; + var paths = new[] { partialViewTemplatePath, partialViewMacrosTemplatePath }; + const string extension = ".cshtml"; + var namesAdded = new List(); + list.Items.Clear(); // always add the options of empty snippets list.Items.Add(new ListItem("Empty", "Empty.cshtml")); list.Items.Add(new ListItem("Empty (For Use With Custom Views)", "Empty (ForUseWithCustomViews).cshtml")); - - if (System.IO.Directory.Exists(path)) + + foreach (var pathFilter in paths) { - const string extension = ".cshtml"; - - //Already adding Empty as the first item, so don't add it again - foreach (var fileInfo in new System.IO.DirectoryInfo(path).GetFiles("*" + extension).Where(f => f.Name.StartsWith("Empty") == false)) + if (Directory.Exists(pathFilter.path) == false) continue; + + var p = pathFilter; + foreach (var fileInfo in new DirectoryInfo(pathFilter.path).GetFiles("*" + extension) + //check if we've already added this name + .Where(f => namesAdded.InvariantContains(f.Name) == false) + //Already adding Empty as the first item, so don't add it again + .Where(f => f.Name.StartsWith("Empty") == false) + //don't add if not in the inclusion list + .Where(f => p.include.Length == 0 || (p.include.Length > 0 && p.include.InvariantContains(f.Name) == false))) { - var filename = System.IO.Path.GetFileName(fileInfo.FullName); - + var filename = Path.GetFileName(fileInfo.FullName); + namesAdded.Add(filename); var liText = filename.Replace(extension, "").SplitPascalCasing().ToFirstUpperInvariant(); list.Items.Add(new ListItem(liText, filename)); } } + + } protected void SubmitButton_Click(object sender, System.EventArgs e) diff --git a/src/Umbraco.Web.UI/config/log4net.Release.config b/src/Umbraco.Web.UI/config/log4net.Release.config index dd9a5e4629..384124e719 100644 --- a/src/Umbraco.Web.UI/config/log4net.Release.config +++ b/src/Umbraco.Web.UI/config/log4net.Release.config @@ -1,25 +1,26 @@ - - - - + + + + - - - + - - - - - - - - - - + + + + + + + + + + + diff --git a/src/Umbraco.Web.UI/config/log4net.config b/src/Umbraco.Web.UI/config/log4net.config index cf73f9afee..10808bb091 100644 --- a/src/Umbraco.Web.UI/config/log4net.config +++ b/src/Umbraco.Web.UI/config/log4net.config @@ -1,25 +1,26 @@ - - - - + + + + - - + - - - - - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/metablogConfig.config b/src/Umbraco.Web.UI/config/metablogConfig.config index 5621dbee75..82502df5e3 100644 --- a/src/Umbraco.Web.UI/config/metablogConfig.config +++ b/src/Umbraco.Web.UI/config/metablogConfig.config @@ -5,7 +5,7 @@ 0 1080 False - Base + umbBlog diff --git a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs b/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs index 6e07894b9f..789145237c 100644 --- a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs +++ b/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs @@ -11,6 +11,8 @@ namespace Umbraco.Web.UI.Umbraco.Create { public partial class PartialViewMacro : UserControl { + + protected override void OnLoad(EventArgs e) { base.OnLoad(e); diff --git a/src/Umbraco.Web.UI/umbraco/plugins/tinymce3/insertImage.aspx b/src/Umbraco.Web.UI/umbraco/plugins/tinymce3/insertImage.aspx index 7d386232d1..6388b243ee 100644 --- a/src/Umbraco.Web.UI/umbraco/plugins/tinymce3/insertImage.aspx +++ b/src/Umbraco.Web.UI/umbraco/plugins/tinymce3/insertImage.aspx @@ -4,6 +4,7 @@ <%@ Register TagPrefix="umb2" TagName="Tree" Src="../../controls/Tree/TreeControl.ascx" %> <%@ Register TagPrefix="umb3" TagName="Image" Src="../../controls/Images/ImageViewer.ascx" %> <%@ Register TagName="MediaUpload" TagPrefix="umb4" Src="../../controls/Images/UploadMediaImage.ascx" %> +<%@ Register TagPrefix="umbClient" Namespace="Umbraco.Web.UI.Bundles" Assembly="umbraco" %> @@ -14,7 +15,10 @@ - + + + + diff --git a/src/Umbraco.Web.UI/umbraco/plugins/tinymce3/insertLink.aspx b/src/Umbraco.Web.UI/umbraco/plugins/tinymce3/insertLink.aspx index 24de8c77b9..a55780b7c3 100644 --- a/src/Umbraco.Web.UI/umbraco/plugins/tinymce3/insertLink.aspx +++ b/src/Umbraco.Web.UI/umbraco/plugins/tinymce3/insertLink.aspx @@ -2,6 +2,7 @@ <%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %> <%@ Register TagPrefix="ui" Namespace="umbraco.uicontrols" Assembly="controls" %> <%@ Register Src="../../controls/Tree/TreeControl.ascx" TagName="TreeControl" TagPrefix="umbraco" %> +<%@ Register TagPrefix="umbClient" Namespace="Umbraco.Web.UI.Bundles" Assembly="umbraco" %> @@ -14,14 +15,18 @@ - + + + + + -