From a8b037f2e0de10b3666b9ec5050dc4c63a47da3d Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 9 Oct 2013 12:09:28 +1100 Subject: [PATCH 01/11] Fixes member type repository when member types don't have properties declared and adds tests. Adds IMemberTypeService. Conflicts: src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs src/Umbraco.Core/Services/ServiceContext.cs --- .../PropertyTypePropertyGroupRelator.cs | 6 ++- .../Repositories/MemberTypeRepository.cs | 4 +- .../Services/IMemberTypeService.cs | 15 ++++++ .../Services/MemberTypeService.cs | 39 ++++++++++++++ src/Umbraco.Core/Services/ServiceContext.cs | 12 +++++ .../Repositories/MemberTypeRepositoryTest.cs | 52 ++++++++++++++++++- 6 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 src/Umbraco.Core/Services/IMemberTypeService.cs create mode 100644 src/Umbraco.Core/Services/MemberTypeService.cs diff --git a/src/Umbraco.Core/Persistence/Relators/PropertyTypePropertyGroupRelator.cs b/src/Umbraco.Core/Persistence/Relators/PropertyTypePropertyGroupRelator.cs index a9129498c5..f02ca8e1b1 100644 --- a/src/Umbraco.Core/Persistence/Relators/PropertyTypePropertyGroupRelator.cs +++ b/src/Umbraco.Core/Persistence/Relators/PropertyTypePropertyGroupRelator.cs @@ -38,7 +38,11 @@ namespace Umbraco.Core.Persistence.Relators // Setup the new current MemberTypeReadOnlyDto Current = a; Current.PropertyTypes = new List(); - Current.PropertyTypes.Add(p); + //this can be null since we are doing a left join + if (p.Id.HasValue) + { + Current.PropertyTypes.Add(p); + } Current.PropertyTypeGroups = new List(); if (g.Id.HasValue) diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs index 9ee215d4f7..73ba016264 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs @@ -108,7 +108,7 @@ namespace Umbraco.Core.Persistence.Repositories .From() .InnerJoin().On(left => left.NodeId, right => right.NodeId) .LeftJoin().On(left => left.ContentTypeId, right => right.NodeId) - .InnerJoin().On(left => left.PropertyTypeId, right => right.Id) + .LeftJoin().On(left => left.PropertyTypeId, right => right.Id) .LeftJoin().On(left => left.DataTypeId, right => right.DataTypeId) .LeftJoin().On(left => left.ContentTypeNodeId, right => right.NodeId) .Where(x => x.NodeObjectType == NodeObjectTypeId); @@ -122,7 +122,7 @@ namespace Umbraco.Core.Persistence.Repositories .From() .InnerJoin().On(left => left.NodeId, right => right.NodeId) .LeftJoin().On(left => left.ContentTypeId, right => right.NodeId) - .InnerJoin().On(left => left.PropertyTypeId, right => right.Id) + .LeftJoin().On(left => left.PropertyTypeId, right => right.Id) .LeftJoin().On(left => left.DataTypeId, right => right.DataTypeId) .LeftJoin().On(left => left.ContentTypeNodeId, right => right.NodeId) .Where(x => x.NodeObjectType == NodeObjectTypeId); diff --git a/src/Umbraco.Core/Services/IMemberTypeService.cs b/src/Umbraco.Core/Services/IMemberTypeService.cs new file mode 100644 index 0000000000..c2cbb8ab40 --- /dev/null +++ b/src/Umbraco.Core/Services/IMemberTypeService.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Services +{ + internal interface IMemberTypeService : IService + { + /// + /// Gets a list of all available objects + /// + /// Optional list of ids + /// An Enumerable list of objects + IEnumerable GetAllMemberTypes(params int[] ids); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/MemberTypeService.cs b/src/Umbraco.Core/Services/MemberTypeService.cs new file mode 100644 index 0000000000..99b90b83d4 --- /dev/null +++ b/src/Umbraco.Core/Services/MemberTypeService.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.UnitOfWork; + +namespace Umbraco.Core.Services +{ + internal class MemberTypeService : IMemberTypeService + { + private readonly IDatabaseUnitOfWorkProvider _uowProvider; + private readonly RepositoryFactory _repositoryFactory; + + public MemberTypeService() + : this(new PetaPocoUnitOfWorkProvider(), new RepositoryFactory()) + {} + + public MemberTypeService(RepositoryFactory repositoryFactory) + : this(new PetaPocoUnitOfWorkProvider(), repositoryFactory) + { } + + public MemberTypeService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory) + { + if (provider == null) throw new ArgumentNullException("provider"); + if (repositoryFactory == null) throw new ArgumentNullException("repositoryFactory"); + _uowProvider = provider; + _repositoryFactory = repositoryFactory; + } + + public IEnumerable GetAllMemberTypes(params int[] ids) + { + using (var repository = _repositoryFactory.CreateMemberTypeRepository(_uowProvider.GetUnitOfWork())) + { + return repository.GetAll(ids); + } + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index cc850d94ba..d2597f0a7a 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -24,6 +24,7 @@ namespace Umbraco.Core.Services private Lazy _serverRegistrationService; private Lazy _entityService; private Lazy _relationService; + private Lazy _memberTypeService; /// /// Constructor @@ -86,6 +87,9 @@ namespace Umbraco.Core.Services if(_relationService == null) _relationService = new Lazy(() => new RelationService(provider, repositoryFactory.Value, _entityService.Value)); + + if (_memberTypeService == null) + _memberTypeService = new Lazy(() => new MemberTypeService(provider, repositoryFactory.Value)); } /// @@ -183,5 +187,13 @@ namespace Umbraco.Core.Services { get { return _memberService.Value; } } + } + + /// + /// Gets the MemberTypeService + /// + internal MemberTypeService MemberTypeService + { + get { return _memberTypeService.Value; } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs index e7a7285f7d..f51e50b57a 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs @@ -45,7 +45,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void MemberRepository_Can_Persist_Member() + public void MemberRepository_Can_Persist_Member_Type() { IMemberType sut; var provider = new PetaPocoUnitOfWorkProvider(); @@ -67,6 +67,56 @@ namespace Umbraco.Tests.Persistence.Repositories } } + [Test] + public void MemberRepository_Can_Get_All_Member_Types() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var memberType1 = MockedContentTypes.CreateSimpleMemberType(); + repository.AddOrUpdate(memberType1); + unitOfWork.Commit(); + + var memberType2 = MockedContentTypes.CreateSimpleMemberType(); + memberType2.Name = "AnotherType"; + memberType2.Alias = "anotherType"; + repository.AddOrUpdate(memberType2); + unitOfWork.Commit(); + + var result = repository.GetAll(); + + //there are 3 because of the Member type created for init data + Assert.AreEqual(3, result.Count()); + } + } + + [Test] + public void MemberRepository_Can_Get_All_Member_When_No_Properties() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var memberType1 = MockedContentTypes.CreateSimpleMemberType(); + memberType1.PropertyTypeCollection.Clear(); + repository.AddOrUpdate(memberType1); + unitOfWork.Commit(); + + var memberType2 = MockedContentTypes.CreateSimpleMemberType(); + memberType2.PropertyTypeCollection.Clear(); + memberType2.Name = "AnotherType"; + memberType2.Alias = "anotherType"; + repository.AddOrUpdate(memberType2); + unitOfWork.Commit(); + + var result = repository.GetAll(); + + //there are 3 because of the Member type created for init data + Assert.AreEqual(3, result.Count()); + } + } + [Test, NUnit.Framework.Ignore] public void MemberTypeRepository_Can_Get_MemberType_By_Id() { From 49f10ccd3201f16ef5f39a6829e49886fb10e487 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 9 Oct 2013 12:18:42 +1100 Subject: [PATCH 02/11] Fixes merge issue , fixes the ServiceContext lazy fields to be the underlying interfaces, adds public ctor --- src/Umbraco.Core/Services/ServiceContext.cs | 136 ++++++++++++-------- src/Umbraco.Core/Umbraco.Core.csproj | 2 + 2 files changed, 85 insertions(+), 53 deletions(-) diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index d2597f0a7a..4e18f16063 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -12,42 +12,71 @@ namespace Umbraco.Core.Services /// public class ServiceContext { - private Lazy _contentService; - private Lazy _userService; - private Lazy _memberService; - private Lazy _mediaService; - private Lazy _contentTypeService; - private Lazy _dataTypeService; - private Lazy _fileService; - private Lazy _localizationService; + private Lazy _contentService; + private Lazy _userService; + private Lazy _memberService; + private Lazy _mediaService; + private Lazy _contentTypeService; + private Lazy _dataTypeService; + private Lazy _fileService; + private Lazy _localizationService; private Lazy _packagingService; private Lazy _serverRegistrationService; private Lazy _entityService; private Lazy _relationService; - private Lazy _memberTypeService; + private Lazy _memberTypeService; - /// - /// Constructor - /// - /// - /// - /// - internal ServiceContext(IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, IUnitOfWorkProvider fileUnitOfWorkProvider, BasePublishingStrategy publishingStrategy) - { - BuildServiceCache(dbUnitOfWorkProvider, fileUnitOfWorkProvider, publishingStrategy, - //this needs to be lazy because when we create the service context it's generally before the - //resolvers have been initialized! - new Lazy(() => RepositoryResolver.Current.Factory)); - } + /// + /// public ctor - will generally just be used for unit testing + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public ServiceContext(Lazy contentService, Lazy mediaService, Lazy contentTypeService, Lazy dataTypeService, Lazy fileService, Lazy localizationService, Lazy packagingService, Lazy serverRegistrationService, Lazy entityService, Lazy relationService, Lazy memberTypeService) + { + _contentService = contentService; + _mediaService = mediaService; + _contentTypeService = contentTypeService; + _dataTypeService = dataTypeService; + _fileService = fileService; + _localizationService = localizationService; + _packagingService = packagingService; + _serverRegistrationService = serverRegistrationService; + _entityService = entityService; + _relationService = relationService; + _memberTypeService = memberTypeService; + } + + /// + /// Constructor used to instantiate the core services + /// + /// + /// + /// + internal ServiceContext(IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, IUnitOfWorkProvider fileUnitOfWorkProvider, BasePublishingStrategy publishingStrategy) + { + BuildServiceCache(dbUnitOfWorkProvider, fileUnitOfWorkProvider, publishingStrategy, + //this needs to be lazy because when we create the service context it's generally before the + //resolvers have been initialized! + new Lazy(() => RepositoryResolver.Current.Factory)); + } /// /// Builds the various services /// - private void BuildServiceCache( - IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, - IUnitOfWorkProvider fileUnitOfWorkProvider, - BasePublishingStrategy publishingStrategy, - Lazy repositoryFactory) + private void BuildServiceCache( + IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, + IUnitOfWorkProvider fileUnitOfWorkProvider, + BasePublishingStrategy publishingStrategy, + Lazy repositoryFactory) { var provider = dbUnitOfWorkProvider; var fileProvider = fileUnitOfWorkProvider; @@ -55,41 +84,41 @@ namespace Umbraco.Core.Services if (_serverRegistrationService == null) _serverRegistrationService = new Lazy(() => new ServerRegistrationService(provider, repositoryFactory.Value)); - if (_userService == null) - _userService = new Lazy(() => new UserService(provider, repositoryFactory.Value)); + if (_userService == null) + _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)); if (_contentService == null) - _contentService = new Lazy(() => new ContentService(provider, repositoryFactory.Value, publishingStrategy)); + _contentService = new Lazy(() => new ContentService(provider, repositoryFactory.Value, publishingStrategy)); - if(_mediaService == null) - _mediaService = new Lazy(() => new MediaService(provider, repositoryFactory.Value)); + if (_mediaService == null) + _mediaService = new Lazy(() => new MediaService(provider, repositoryFactory.Value)); - if(_contentTypeService == null) - _contentTypeService = new Lazy(() => new ContentTypeService(provider, repositoryFactory.Value, _contentService.Value, _mediaService.Value)); + if (_contentTypeService == null) + _contentTypeService = new Lazy(() => new ContentTypeService(provider, repositoryFactory.Value, _contentService.Value, _mediaService.Value)); - if(_dataTypeService == null) - _dataTypeService = new Lazy(() => new DataTypeService(provider, repositoryFactory.Value)); + if (_dataTypeService == null) + _dataTypeService = new Lazy(() => new DataTypeService(provider, repositoryFactory.Value)); - if(_fileService == null) - _fileService = new Lazy(() => new FileService(fileProvider, provider, repositoryFactory.Value)); + if (_fileService == null) + _fileService = new Lazy(() => new FileService(fileProvider, provider, repositoryFactory.Value)); - if(_localizationService == null) - _localizationService = new Lazy(() => new LocalizationService(provider, repositoryFactory.Value)); + if (_localizationService == null) + _localizationService = new Lazy(() => new LocalizationService(provider, repositoryFactory.Value)); - if(_packagingService == null) + if (_packagingService == null) _packagingService = new Lazy(() => new PackagingService(_contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value, _fileService.Value, _localizationService.Value, repositoryFactory.Value, provider)); if (_entityService == null) _entityService = new Lazy(() => new EntityService(provider, repositoryFactory.Value, _contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value)); - if(_relationService == null) + if (_relationService == null) _relationService = new Lazy(() => new RelationService(provider, repositoryFactory.Value, _entityService.Value)); if (_memberTypeService == null) - _memberTypeService = new Lazy(() => new MemberTypeService(provider, repositoryFactory.Value)); + _memberTypeService = new Lazy(() => new MemberTypeService(provider, repositoryFactory.Value)); } /// @@ -129,7 +158,7 @@ namespace Umbraco.Core.Services /// public IContentTypeService ContentTypeService { - get { return _contentTypeService.Value; } + get { return _contentTypeService.Value; } } /// @@ -137,7 +166,7 @@ namespace Umbraco.Core.Services /// public IDataTypeService DataTypeService { - get { return _dataTypeService.Value; } + get { return _dataTypeService.Value; } } /// @@ -145,7 +174,7 @@ namespace Umbraco.Core.Services /// public IFileService FileService { - get { return _fileService.Value; } + get { return _fileService.Value; } } /// @@ -153,7 +182,7 @@ namespace Umbraco.Core.Services /// public ILocalizationService LocalizationService { - get { return _localizationService.Value; } + get { return _localizationService.Value; } } /// @@ -161,7 +190,7 @@ namespace Umbraco.Core.Services /// public IMediaService MediaService { - get { return _mediaService.Value; } + get { return _mediaService.Value; } } /// @@ -177,7 +206,7 @@ namespace Umbraco.Core.Services /// internal IUserService UserService { - get { return _userService.Value; } + get { return _userService.Value; } } /// @@ -187,13 +216,14 @@ namespace Umbraco.Core.Services { get { return _memberService.Value; } } - } - + /// /// Gets the MemberTypeService /// - internal MemberTypeService MemberTypeService + internal IMemberTypeService MemberTypeService { get { return _memberTypeService.Value; } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 1386f52e2a..53672bb4aa 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -719,11 +719,13 @@ + + From c6389852ea8dddccc19772ae4fc69cb5a9db723d Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 9 Oct 2013 13:17:19 +1100 Subject: [PATCH 03/11] Adds public ctors for ApplicationContext, ServiceContext, DatabaseContext, RepositoryFactory, makes IDatabaseFactory public, makes an interfaces for IEntityService, adds EnsureContext methods for ApplicationContext - to set the singleton. --- src/Umbraco.Core/ApplicationContext.cs | 57 ++++++- src/Umbraco.Core/DatabaseContext.cs | 2 +- .../Persistence/IDatabaseFactory.cs | 2 +- .../Persistence/RepositoryFactory.cs | 2 +- src/Umbraco.Core/Services/EntityService.cs | 2 +- src/Umbraco.Core/Services/IEntityService.cs | 146 ++++++++++++++++++ src/Umbraco.Core/Services/RelationService.cs | 4 +- src/Umbraco.Core/Services/ServiceContext.cs | 30 ++-- src/Umbraco.Core/Umbraco.Core.csproj | 1 + src/Umbraco.Tests/MockTests.cs | 124 +++++++++++++++ .../{ => Persistence}/Auditing/AuditTests.cs | 68 ++++---- .../Routing/RenderRouteHandlerTests.cs | 2 +- .../Routing/uQueryGetNodeIdByUrlTests.cs | 2 +- .../TestHelpers/BaseDatabaseFactoryTest.cs | 1 - .../TestHelpers/BaseRoutingTest.cs | 2 +- src/Umbraco.Tests/TestHelpers/BaseWebTest.cs | 1 - .../Stubs/FakeLastChanceFinder.cs | 22 +-- .../Stubs/TestControllerFactory.cs | 78 +++++----- src/Umbraco.Tests/Umbraco.Tests.csproj | 7 +- 19 files changed, 432 insertions(+), 121 deletions(-) create mode 100644 src/Umbraco.Core/Services/IEntityService.cs create mode 100644 src/Umbraco.Tests/MockTests.cs rename src/Umbraco.Tests/{ => Persistence}/Auditing/AuditTests.cs (92%) rename src/Umbraco.Tests/{ => TestHelpers}/Stubs/FakeLastChanceFinder.cs (81%) rename src/Umbraco.Tests/{ => TestHelpers}/Stubs/TestControllerFactory.cs (94%) diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs index 78ed2c755a..ed3a0d127d 100644 --- a/src/Umbraco.Core/ApplicationContext.cs +++ b/src/Umbraco.Core/ApplicationContext.cs @@ -35,12 +35,9 @@ namespace Umbraco.Core /// /// /// - internal ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext, bool enableCache) + public ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext, bool enableCache) : this(enableCache) { - if (dbContext == null) throw new ArgumentNullException("dbContext"); - if (serviceContext == null) throw new ArgumentNullException("serviceContext"); - _databaseContext = dbContext; _services = serviceContext; } @@ -57,7 +54,7 @@ namespace Umbraco.Core /// Constructor used to specify if we will enable application cache or not /// /// - internal ApplicationContext(bool enableCache) + public ApplicationContext(bool enableCache) { //create a new application cache from the HttpRuntime.Cache ApplicationCache = HttpRuntime.Cache == null @@ -65,7 +62,55 @@ namespace Umbraco.Core : new CacheHelper(HttpRuntime.Cache, enableCache); } - /// + /// + /// A method used to set and/or ensure that a global ApplicationContext singleton is created. + /// + /// + /// The instance to set on the global application singleton + /// + /// If set to true and the singleton is already set, it will be replaced + /// + /// + /// This is NOT thread safe + /// + public static ApplicationContext EnsureContext(ApplicationContext appContext, bool replaceContext) + { + if (ApplicationContext.Current != null) + { + if (!replaceContext) + return ApplicationContext.Current; + } + ApplicationContext.Current = appContext; + return ApplicationContext.Current; + } + + /// + /// A method used to create and ensure that a global ApplicationContext singleton is created. + /// + /// + /// + /// If set to true will replace the current singleton instance - This should only be used for unit tests or on app + /// startup if for some reason the boot manager is not the umbraco boot manager. + /// + /// + /// + /// + /// + /// This is NOT thread safe + /// + public static ApplicationContext EnsureContext(DatabaseContext dbContext, ServiceContext serviceContext, bool enableCache, bool replaceContext) + { + if (ApplicationContext.Current != null) + { + if (!replaceContext) + return ApplicationContext.Current; + } + var ctx = new ApplicationContext(dbContext, serviceContext, enableCache); + ApplicationContext.Current = ctx; + return ApplicationContext.Current; + } + + /// /// Singleton accessor /// public static ApplicationContext Current { get; internal set; } diff --git a/src/Umbraco.Core/DatabaseContext.cs b/src/Umbraco.Core/DatabaseContext.cs index dbbc476ae9..4d5170ec80 100644 --- a/src/Umbraco.Core/DatabaseContext.cs +++ b/src/Umbraco.Core/DatabaseContext.cs @@ -28,7 +28,7 @@ namespace Umbraco.Core private string _providerName; private DatabaseSchemaResult _result; - internal DatabaseContext(IDatabaseFactory factory) + public DatabaseContext(IDatabaseFactory factory) { _factory = factory; } diff --git a/src/Umbraco.Core/Persistence/IDatabaseFactory.cs b/src/Umbraco.Core/Persistence/IDatabaseFactory.cs index 87c146fa0f..b0efb7f94a 100644 --- a/src/Umbraco.Core/Persistence/IDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/IDatabaseFactory.cs @@ -5,7 +5,7 @@ namespace Umbraco.Core.Persistence /// /// Used to create the UmbracoDatabase for use in the DatabaseContext /// - internal interface IDatabaseFactory : IDisposable + public interface IDatabaseFactory : IDisposable { UmbracoDatabase CreateDatabase(); } diff --git a/src/Umbraco.Core/Persistence/RepositoryFactory.cs b/src/Umbraco.Core/Persistence/RepositoryFactory.cs index 710dc27d87..ab0a5e8e65 100644 --- a/src/Umbraco.Core/Persistence/RepositoryFactory.cs +++ b/src/Umbraco.Core/Persistence/RepositoryFactory.cs @@ -17,7 +17,7 @@ namespace Umbraco.Core.Persistence } - internal RepositoryFactory(bool disableAllCache) + public RepositoryFactory(bool disableAllCache) { _disableAllCache = disableAllCache; } diff --git a/src/Umbraco.Core/Services/EntityService.cs b/src/Umbraco.Core/Services/EntityService.cs index 882aa86b4e..7e103c687b 100644 --- a/src/Umbraco.Core/Services/EntityService.cs +++ b/src/Umbraco.Core/Services/EntityService.cs @@ -10,7 +10,7 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { - public class EntityService : IService + public class EntityService : IService, IEntityService { private readonly IDatabaseUnitOfWorkProvider _uowProvider; private readonly RepositoryFactory _repositoryFactory; diff --git a/src/Umbraco.Core/Services/IEntityService.cs b/src/Umbraco.Core/Services/IEntityService.cs new file mode 100644 index 0000000000..c32d755c3f --- /dev/null +++ b/src/Umbraco.Core/Services/IEntityService.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Services +{ + public interface IEntityService + { + /// + /// Gets an UmbracoEntity by its Id, and optionally loads the complete object graph. + /// + /// + /// By default this will load the base type with a minimum set of properties. + /// + /// Id of the object to retrieve + /// Optional bool to load the complete object graph when set to False. + /// An + IUmbracoEntity Get(int id, bool loadBaseType = true); + + /// + /// Gets an UmbracoEntity by its Id and UmbracoObjectType, and optionally loads the complete object graph. + /// + /// + /// By default this will load the base type with a minimum set of properties. + /// + /// Id of the object to retrieve + /// UmbracoObjectType of the entity to retrieve + /// Optional bool to load the complete object graph when set to False. + /// An + IUmbracoEntity Get(int id, UmbracoObjectTypes umbracoObjectType, bool loadBaseType = true); + + /// + /// Gets an UmbracoEntity by its Id and specified Type. Optionally loads the complete object graph. + /// + /// + /// By default this will load the base type with a minimum set of properties. + /// + /// Type of the model to retrieve. Must be based on an + /// Id of the object to retrieve + /// Optional bool to load the complete object graph when set to False. + /// An + IUmbracoEntity Get(int id, bool loadBaseType = true) where T : IUmbracoEntity; + + /// + /// Gets the parent of entity by its id + /// + /// Id of the entity to retrieve the Parent for + /// An + IUmbracoEntity GetParent(int id); + + /// + /// Gets the parent of entity by its id and UmbracoObjectType + /// + /// Id of the entity to retrieve the Parent for + /// UmbracoObjectType of the parent to retrieve + /// An + IUmbracoEntity GetParent(int id, UmbracoObjectTypes umbracoObjectType); + + /// + /// Gets a collection of children by the parents Id + /// + /// Id of the parent to retrieve children for + /// An enumerable list of objects + IEnumerable GetChildren(int parentId); + + /// + /// Gets a collection of children by the parents Id and UmbracoObjectType + /// + /// Id of the parent to retrieve children for + /// UmbracoObjectType of the children to retrieve + /// An enumerable list of objects + IEnumerable GetChildren(int parentId, UmbracoObjectTypes umbracoObjectType); + + /// + /// Gets a collection of descendents by the parents Id + /// + /// Id of entity to retrieve descendents for + /// An enumerable list of objects + IEnumerable GetDescendents(int id); + + /// + /// Gets a collection of descendents by the parents Id + /// + /// Id of entity to retrieve descendents for + /// UmbracoObjectType of the descendents to retrieve + /// An enumerable list of objects + IEnumerable GetDescendents(int id, UmbracoObjectTypes umbracoObjectType); + + /// + /// Gets a collection of the entities at the root, which corresponds to the entities with a Parent Id of -1. + /// + /// UmbracoObjectType of the root entities to retrieve + /// An enumerable list of objects + IEnumerable GetRootEntities(UmbracoObjectTypes umbracoObjectType); + + /// + /// Gets a collection of all of a given type. + /// + /// Type of the entities to retrieve + /// An enumerable list of objects + IEnumerable GetAll() where T : IUmbracoEntity; + + /// + /// Gets a collection of all of a given type. + /// + /// UmbracoObjectType of the entities to return + /// An enumerable list of objects + IEnumerable GetAll(UmbracoObjectTypes umbracoObjectType); + + /// + /// Gets a collection of + /// + /// Guid id of the UmbracoObjectType + /// An enumerable list of objects + IEnumerable GetAll(Guid objectTypeId); + + /// + /// Gets the UmbracoObjectType from the integer id of an IUmbracoEntity. + /// + /// Id of the entity + /// + UmbracoObjectTypes GetObjectType(int id); + + /// + /// Gets the UmbracoObjectType from an IUmbracoEntity. + /// + /// + /// + UmbracoObjectTypes GetObjectType(IUmbracoEntity entity); + + /// + /// Gets the Type of an entity by its Id + /// + /// Id of the entity + /// Type of the entity + Type GetEntityType(int id); + + /// + /// Gets the Type of an entity by its + /// + /// + /// Type of the entity + Type GetEntityType(UmbracoObjectTypes umbracoObjectType); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/RelationService.cs b/src/Umbraco.Core/Services/RelationService.cs index 83d09023ab..29a42da115 100644 --- a/src/Umbraco.Core/Services/RelationService.cs +++ b/src/Umbraco.Core/Services/RelationService.cs @@ -13,10 +13,10 @@ namespace Umbraco.Core.Services { private readonly IDatabaseUnitOfWorkProvider _uowProvider; private readonly RepositoryFactory _repositoryFactory; - private readonly EntityService _entityService; + private readonly IEntityService _entityService; public RelationService(IDatabaseUnitOfWorkProvider uowProvider, RepositoryFactory repositoryFactory, - EntityService entityService) + IEntityService entityService) { _uowProvider = uowProvider; _repositoryFactory = repositoryFactory; diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index 4e18f16063..b6ccfc3d81 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -22,7 +22,7 @@ namespace Umbraco.Core.Services private Lazy _localizationService; private Lazy _packagingService; private Lazy _serverRegistrationService; - private Lazy _entityService; + private Lazy _entityService; private Lazy _relationService; private Lazy _memberTypeService; @@ -36,23 +36,19 @@ namespace Umbraco.Core.Services /// /// /// - /// /// /// - /// - public ServiceContext(Lazy contentService, Lazy mediaService, Lazy contentTypeService, Lazy dataTypeService, Lazy fileService, Lazy localizationService, Lazy packagingService, Lazy serverRegistrationService, Lazy entityService, Lazy relationService, Lazy memberTypeService) + public ServiceContext(IContentService contentService, IMediaService mediaService, IContentTypeService contentTypeService, IDataTypeService dataTypeService, IFileService fileService, ILocalizationService localizationService, PackagingService packagingService, IEntityService entityService, RelationService relationService) { - _contentService = contentService; - _mediaService = mediaService; - _contentTypeService = contentTypeService; - _dataTypeService = dataTypeService; - _fileService = fileService; - _localizationService = localizationService; - _packagingService = packagingService; - _serverRegistrationService = serverRegistrationService; - _entityService = entityService; - _relationService = relationService; - _memberTypeService = memberTypeService; + _contentService = new Lazy(() => contentService); + _mediaService = new Lazy(() => mediaService); + _contentTypeService = new Lazy(() => contentTypeService); + _dataTypeService = new Lazy(() => dataTypeService); + _fileService = new Lazy(() => fileService); + _localizationService = new Lazy(() => localizationService); + _packagingService = new Lazy(() => packagingService); + _entityService = new Lazy(() => entityService); + _relationService = new Lazy(() => relationService); } /// @@ -112,7 +108,7 @@ namespace Umbraco.Core.Services _packagingService = new Lazy(() => new PackagingService(_contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value, _fileService.Value, _localizationService.Value, repositoryFactory.Value, provider)); if (_entityService == null) - _entityService = new Lazy(() => new EntityService(provider, repositoryFactory.Value, _contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value)); + _entityService = new Lazy(() => new EntityService(provider, repositoryFactory.Value, _contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value)); if (_relationService == null) _relationService = new Lazy(() => new RelationService(provider, repositoryFactory.Value, _entityService.Value)); @@ -132,7 +128,7 @@ namespace Umbraco.Core.Services /// /// Gets the /// - public EntityService EntityService + public IEntityService EntityService { get { return _entityService.Value; } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 53672bb4aa..5db9dd7d56 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -714,6 +714,7 @@ + diff --git a/src/Umbraco.Tests/MockTests.cs b/src/Umbraco.Tests/MockTests.cs new file mode 100644 index 0000000000..be31d2dbf4 --- /dev/null +++ b/src/Umbraco.Tests/MockTests.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Services; +using Moq; +using Umbraco.Web; + +namespace Umbraco.Tests +{ + [TestFixture] + public class MockTests + { + + [Test] + public void Can_Create_Empty_App_Context() + { + var appCtx = new ApplicationContext(false); + Assert.Pass(); + } + + [Test] + public void Can_Create_Service_Context() + { + var svcCtx = new ServiceContext( + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new PackagingService( + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new RepositoryFactory(true), + new Mock().Object), + new Mock().Object, + new RelationService( + new Mock().Object, + new RepositoryFactory(true), + new Mock().Object)); + Assert.Pass(); + } + + [Test] + public void Can_Create_Db_Context() + { + var dbCtx = new DatabaseContext(new Mock().Object); + Assert.Pass(); + } + + [Test] + public void Can_Create_App_Context_With_Services() + { + var appCtx = new ApplicationContext( + new DatabaseContext(new Mock().Object), + new ServiceContext( + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new PackagingService( + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new RepositoryFactory(true), + new Mock().Object), + new Mock().Object, + new RelationService( + new Mock().Object, + new RepositoryFactory(true), + new Mock().Object)), + false); + Assert.Pass(); + } + + [Test] + public void Can_Assign_App_Context_Singleton() + { + var appCtx = new ApplicationContext(false); + var result = ApplicationContext.EnsureContext(appCtx, true); + Assert.AreEqual(appCtx, result); + } + + [Test] + public void Does_Not_Overwrite_App_Context_Singleton() + { + ApplicationContext.EnsureContext(new ApplicationContext(false), true); + var appCtx = new ApplicationContext(false); + var result = ApplicationContext.EnsureContext(appCtx, false); + Assert.AreNotEqual(appCtx, result); + } + + [NUnit.Framework.Ignore("Need to fix more stuff up, this is ignore because an exception occurs because it wants to ensure we have a resolver initialized - need to make that process better for testability")] + [Test] + public void Can_Get_Umbraco_Context() + { + var appCtx = new ApplicationContext(false); + ApplicationContext.EnsureContext(appCtx, true); + + var umbCtx = UmbracoContext.EnsureContext( + new Mock().Object, + appCtx, + true); + + Assert.AreEqual(umbCtx, UmbracoContext.Current); + } + + } +} diff --git a/src/Umbraco.Tests/Auditing/AuditTests.cs b/src/Umbraco.Tests/Persistence/Auditing/AuditTests.cs similarity index 92% rename from src/Umbraco.Tests/Auditing/AuditTests.cs rename to src/Umbraco.Tests/Persistence/Auditing/AuditTests.cs index ff5e6bb164..68e7bcb003 100644 --- a/src/Umbraco.Tests/Auditing/AuditTests.cs +++ b/src/Umbraco.Tests/Persistence/Auditing/AuditTests.cs @@ -1,35 +1,35 @@ -using System.Linq; -using NUnit.Framework; -using Umbraco.Core.Auditing; -using Umbraco.Core.Models.Rdbms; -using Umbraco.Tests.TestHelpers; - -namespace Umbraco.Tests.Auditing -{ - [TestFixture] - public class AuditTests : BaseDatabaseFactoryTest - { - [SetUp] - public override void Initialize() - { - base.Initialize(); - } - - [Test] - public void Can_Add_Audit_Entry() - { - Audit.Add(AuditTypes.System, "This is a System audit trail", 0, -1); - - var dtos = DatabaseContext.Database.Fetch("WHERE id > -1"); - - Assert.That(dtos.Any(), Is.True); - Assert.That(dtos.First().Comment, Is.EqualTo("This is a System audit trail")); - } - - [TearDown] - public override void TearDown() - { - base.TearDown(); - } - } +using System.Linq; +using NUnit.Framework; +using Umbraco.Core.Auditing; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Tests.TestHelpers; + +namespace Umbraco.Tests.Persistence.Auditing +{ + [TestFixture] + public class AuditTests : BaseDatabaseFactoryTest + { + [SetUp] + public override void Initialize() + { + base.Initialize(); + } + + [Test] + public void Can_Add_Audit_Entry() + { + Audit.Add(AuditTypes.System, "This is a System audit trail", 0, -1); + + var dtos = DatabaseContext.Database.Fetch("WHERE id > -1"); + + Assert.That(dtos.Any(), Is.True); + Assert.That(dtos.First().Comment, Is.EqualTo("This is a System audit trail")); + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index 2b00ea75cf..4b13acf7ee 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -3,8 +3,8 @@ using System.Web.Routing; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Tests.Stubs; using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.Models; using Umbraco.Web.Mvc; diff --git a/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs b/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs index 24bc912f91..53d5301246 100644 --- a/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs +++ b/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using System.Linq; using System.Text; using NUnit.Framework; -using Umbraco.Tests.Stubs; using Umbraco.Tests.TestHelpers; using System.Configuration; +using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Web.Routing; diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index 00896040a9..57355f3e7a 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -19,7 +19,6 @@ using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Publishing; using Umbraco.Core.Services; -using Umbraco.Tests.Stubs; using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.PublishedCache.XmlPublishedCache; diff --git a/src/Umbraco.Tests/TestHelpers/BaseRoutingTest.cs b/src/Umbraco.Tests/TestHelpers/BaseRoutingTest.cs index ab787bf105..d55a437976 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseRoutingTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseRoutingTest.cs @@ -4,7 +4,7 @@ using System.Web.Routing; using NUnit.Framework; using Umbraco.Core.Configuration; using Umbraco.Core.Models; -using Umbraco.Tests.Stubs; +using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Web.Routing; diff --git a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs index 95e89468c3..a635a42269 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs @@ -12,7 +12,6 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Publishing; using Umbraco.Core.Services; -using Umbraco.Tests.Stubs; using Umbraco.Web; using Umbraco.Web.Routing; using umbraco.BusinessLogic; diff --git a/src/Umbraco.Tests/Stubs/FakeLastChanceFinder.cs b/src/Umbraco.Tests/TestHelpers/Stubs/FakeLastChanceFinder.cs similarity index 81% rename from src/Umbraco.Tests/Stubs/FakeLastChanceFinder.cs rename to src/Umbraco.Tests/TestHelpers/Stubs/FakeLastChanceFinder.cs index 9090909baa..c9f5dbe024 100644 --- a/src/Umbraco.Tests/Stubs/FakeLastChanceFinder.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/FakeLastChanceFinder.cs @@ -1,12 +1,12 @@ -using Umbraco.Web.Routing; - -namespace Umbraco.Tests.Stubs -{ - internal class FakeLastChanceFinder : IContentFinder - { - public bool TryFindContent(PublishedContentRequest docRequest) - { - return false; - } - } +using Umbraco.Web.Routing; + +namespace Umbraco.Tests.TestHelpers.Stubs +{ + internal class FakeLastChanceFinder : IContentFinder + { + public bool TryFindContent(PublishedContentRequest docRequest) + { + return false; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Stubs/TestControllerFactory.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs similarity index 94% rename from src/Umbraco.Tests/Stubs/TestControllerFactory.cs rename to src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs index 872093f896..5796ee5bc3 100644 --- a/src/Umbraco.Tests/Stubs/TestControllerFactory.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs @@ -1,40 +1,40 @@ -using System; -using System.Linq; -using System.Reflection; -using System.Web.Mvc; -using System.Web.Routing; -using System.Web.SessionState; -using Umbraco.Core; - -namespace Umbraco.Tests.Stubs -{ - /// - /// Used in place of the UmbracoControllerFactory which relies on BuildManager which throws exceptions in a unit test context - /// - internal class TestControllerFactory : IControllerFactory - { - - public IController CreateController(RequestContext requestContext, string controllerName) - { - var types = TypeFinder.FindClassesOfType(new[] { Assembly.GetExecutingAssembly() }); - - var controllerTypes = types.Where(x => x.Name.Equals(controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase)); - var t = controllerTypes.SingleOrDefault(); - - if (t == null) - return null; - - return Activator.CreateInstance(t) as IController; - } - - public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName) - { - return SessionStateBehavior.Disabled; - } - - public void ReleaseController(IController controller) - { - controller.DisposeIfDisposable(); - } - } +using System; +using System.Linq; +using System.Reflection; +using System.Web.Mvc; +using System.Web.Routing; +using System.Web.SessionState; +using Umbraco.Core; + +namespace Umbraco.Tests.TestHelpers.Stubs +{ + /// + /// Used in place of the UmbracoControllerFactory which relies on BuildManager which throws exceptions in a unit test context + /// + internal class TestControllerFactory : IControllerFactory + { + + public IController CreateController(RequestContext requestContext, string controllerName) + { + var types = TypeFinder.FindClassesOfType(new[] { Assembly.GetExecutingAssembly() }); + + var controllerTypes = types.Where(x => x.Name.Equals(controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase)); + var t = controllerTypes.SingleOrDefault(); + + if (t == null) + return null; + + return Activator.CreateInstance(t) as IController; + } + + public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName) + { + return SessionStateBehavior.Disabled; + } + + public void ReleaseController(IController controller) + { + controller.DisposeIfDisposable(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 469caa926c..da950ed3b1 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -144,7 +144,8 @@ - + + @@ -355,7 +356,7 @@ - + @@ -391,7 +392,7 @@ - + From 09b71867136e29de33ff5260d4ced7593c442e93 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 9 Oct 2013 13:32:58 +1100 Subject: [PATCH 04/11] Completes: U4-2963 Updated EntityService / IUmbracoEntity to have a dictionary to fill additional data - fixes merge issues and explicitly implements interface for entities that are not UmbracoEntity and hides other implementations from intellisense. --- src/Umbraco.Core/Models/ContentBase.cs | 13 +++++ src/Umbraco.Core/Models/ContentTypeBase.cs | 13 +++++ src/Umbraco.Core/Models/DataTypeDefinition.cs | 23 +++++++- .../Models/EntityBase/IUmbracoEntity.cs | 9 +++- src/Umbraco.Core/Models/UmbracoEntity.cs | 36 ++++++++++++- .../Persistence/Factories/IEntityFactory.cs | 2 + .../Factories/UmbracoEntityFactory.cs | 39 ++++++++++++++ .../Repositories/EntityRepository.cs | 54 +++++++++++++++---- src/Umbraco.Core/TypeHelper.cs | 47 ++++++++++++++++ 9 files changed, 223 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index 738a896155..9a5e735e9e 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; +using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Linq; @@ -50,6 +51,7 @@ namespace Umbraco.Core.Models _contentTypeId = int.Parse(contentType.Id.ToString(CultureInfo.InvariantCulture)); _properties = properties; _properties.EnsurePropertyTypes(PropertyTypes); + _additionalData = new Dictionary(); } /// @@ -73,6 +75,7 @@ namespace Umbraco.Core.Models _contentTypeId = int.Parse(contentType.Id.ToString(CultureInfo.InvariantCulture)); _properties = properties; _properties.EnsurePropertyTypes(PropertyTypes); + _additionalData = new Dictionary(); } private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); @@ -253,6 +256,16 @@ namespace Umbraco.Core.Models } } + private readonly IDictionary _additionalData; + /// + /// Some entities may expose additional data that other's might not, this custom data will be available in this collection + /// + [EditorBrowsable(EditorBrowsableState.Never)] + IDictionary IUmbracoEntity.AdditionalData + { + get { return _additionalData; } + } + /// /// List of PropertyGroups available on this Content object /// diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index e8d2effbc1..94000e41c9 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; +using System.ComponentModel; using System.Linq; using System.Reflection; using System.Runtime.Serialization; @@ -42,6 +43,7 @@ namespace Umbraco.Core.Models _allowedContentTypes = new List(); _propertyGroups = new PropertyGroupCollection(); _propertyTypes = new PropertyTypeCollection(); + _additionalData = new Dictionary(); } protected ContentTypeBase(IContentTypeBase parent) @@ -52,6 +54,7 @@ namespace Umbraco.Core.Models _allowedContentTypes = new List(); _propertyGroups = new PropertyGroupCollection(); _propertyTypes = new PropertyTypeCollection(); + _additionalData = new Dictionary(); } private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); @@ -314,6 +317,16 @@ namespace Umbraco.Core.Models } } + private readonly IDictionary _additionalData; + /// + /// Some entities may expose additional data that other's might not, this custom data will be available in this collection + /// + [EditorBrowsable(EditorBrowsableState.Never)] + IDictionary IUmbracoEntity.AdditionalData + { + get { return _additionalData; } + } + /// /// Gets or sets a list of integer Ids for allowed ContentTypes /// diff --git a/src/Umbraco.Core/Models/DataTypeDefinition.cs b/src/Umbraco.Core/Models/DataTypeDefinition.cs index f7068a3b6e..387101f5d8 100644 --- a/src/Umbraco.Core/Models/DataTypeDefinition.cs +++ b/src/Umbraco.Core/Models/DataTypeDefinition.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.ComponentModel; using System.Reflection; using System.Runtime.Serialization; using Umbraco.Core.Models.EntityBase; @@ -31,6 +33,8 @@ namespace Umbraco.Core.Models { _parentId = parentId; _controlId = controlId; + _additionalData = new Dictionary(); + _additionalData = new Dictionary(); } private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); @@ -160,9 +164,11 @@ namespace Umbraco.Core.Models _trashed = value; return _trashed; }, _trashed, TrashedSelector); + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + _additionalData["Trashed"] = value; } } - + /// /// Id of the DataType control /// @@ -177,6 +183,8 @@ namespace Umbraco.Core.Models _controlId = value; return _controlId; }, _controlId, ControlIdSelector); + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + _additionalData["ControlId"] = value; } } @@ -194,9 +202,22 @@ namespace Umbraco.Core.Models _databaseType = value; return _databaseType; }, _databaseType, DatabaseTypeSelector); + + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + _additionalData["DatabaseType"] = value; } } + private readonly IDictionary _additionalData; + /// + /// Some entities may expose additional data that other's might not, this custom data will be available in this collection + /// + [EditorBrowsable(EditorBrowsableState.Never)] + IDictionary IUmbracoEntity.AdditionalData + { + get { return _additionalData; } + } + internal override void AddingEntity() { base.AddingEntity(); diff --git a/src/Umbraco.Core/Models/EntityBase/IUmbracoEntity.cs b/src/Umbraco.Core/Models/EntityBase/IUmbracoEntity.cs index 62130fdab9..397f4518a9 100644 --- a/src/Umbraco.Core/Models/EntityBase/IUmbracoEntity.cs +++ b/src/Umbraco.Core/Models/EntityBase/IUmbracoEntity.cs @@ -1,4 +1,6 @@ -namespace Umbraco.Core.Models.EntityBase +using System.Collections.Generic; + +namespace Umbraco.Core.Models.EntityBase { public interface IUmbracoEntity : IAggregateRoot { @@ -41,5 +43,10 @@ /// Not all entities support being trashed, they'll always return false. /// bool Trashed { get; } + + /// + /// 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/UmbracoEntity.cs b/src/Umbraco.Core/Models/UmbracoEntity.cs index 451c0fe9c1..0a8302dea1 100644 --- a/src/Umbraco.Core/Models/UmbracoEntity.cs +++ b/src/Umbraco.Core/Models/UmbracoEntity.cs @@ -44,10 +44,12 @@ namespace Umbraco.Core.Models public UmbracoEntity() { + AdditionalData = new Dictionary(); } public UmbracoEntity(bool trashed) { + AdditionalData = new Dictionary(); Trashed = trashed; } @@ -142,6 +144,9 @@ namespace Umbraco.Core.Models } } + public IDictionary AdditionalData { get; private set; } + + public bool HasChildren { get { return _hasChildren; } @@ -152,6 +157,9 @@ namespace Umbraco.Core.Models _hasChildren = value; return _hasChildren; }, _hasChildren, HasChildrenSelector); + + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + AdditionalData["HasChildren"] = value; } } @@ -164,7 +172,10 @@ namespace Umbraco.Core.Models { _isPublished = value; return _isPublished; - }, _isPublished, IsPublishedSelector); + }, _isPublished, IsPublishedSelector); + + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + AdditionalData["IsPublished"] = value; } } @@ -178,6 +189,9 @@ namespace Umbraco.Core.Models _isDraft = value; return _isDraft; }, _isDraft, IsDraftSelector); + + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + AdditionalData["IsDraft"] = value; } } @@ -191,6 +205,9 @@ namespace Umbraco.Core.Models _hasPendingChanges = value; return _hasPendingChanges; }, _hasPendingChanges, HasPendingChangesSelector); + + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + AdditionalData["HasPendingChanges"] = value; } } @@ -204,6 +221,9 @@ namespace Umbraco.Core.Models _contentTypeAlias = value; return _contentTypeAlias; }, _contentTypeAlias, ContentTypeAliasSelector); + + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + AdditionalData["ContentTypeAlias"] = value; } } @@ -217,6 +237,9 @@ namespace Umbraco.Core.Models _contentTypeIcon = value; return _contentTypeIcon; }, _contentTypeIcon, ContentTypeIconSelector); + + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + AdditionalData["ContentTypeIcon"] = value; } } @@ -230,6 +253,9 @@ namespace Umbraco.Core.Models _contentTypeThumbnail = value; return _contentTypeThumbnail; }, _contentTypeThumbnail, ContentTypeThumbnailSelector); + + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + AdditionalData["ContentTypeThumbnail"] = value; } } @@ -242,10 +268,16 @@ namespace Umbraco.Core.Models { _nodeObjectTypeId = value; return _nodeObjectTypeId; - }, _nodeObjectTypeId, NodeObjectTypeIdSelector); + }, _nodeObjectTypeId, NodeObjectTypeIdSelector); + + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + AdditionalData["NodeObjectTypeId"] = value; } } + /// + /// Some entities may expose additional data that other's might not, this custom data will be available in this collection + /// public IList UmbracoProperties { get; set; } internal class UmbracoProperty diff --git a/src/Umbraco.Core/Persistence/Factories/IEntityFactory.cs b/src/Umbraco.Core/Persistence/Factories/IEntityFactory.cs index 6053090178..a0d8823d68 100644 --- a/src/Umbraco.Core/Persistence/Factories/IEntityFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/IEntityFactory.cs @@ -1,5 +1,7 @@ namespace Umbraco.Core.Persistence.Factories { + //TODO: Not sure why we need this interface as it's never referenced in code. + internal interface IEntityFactory where TEntity : class where TDto : class diff --git a/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs b/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs index d7b8247364..226294be0f 100644 --- a/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs @@ -8,6 +8,45 @@ namespace Umbraco.Core.Persistence.Factories { internal class UmbracoEntityFactory : IEntityFactory { + internal UmbracoEntity BuildEntityFromDynamic(dynamic d) + { + var entity = new UmbracoEntity(d.trashed) + { + CreateDate = d.createDate, + CreatorId = d.nodeUser, + Id = d.id, + Key = d.uniqueID, + Level = d.level, + Name = d.text, + NodeObjectTypeId = d.nodeObjectType, + ParentId = d.parentID, + Path = d.path, + SortOrder = d.sortOrder, + HasChildren = d.children > 0, + ContentTypeAlias = d.alias ?? string.Empty, + ContentTypeIcon = d.icon ?? string.Empty, + ContentTypeThumbnail = d.thumbnail ?? string.Empty, + UmbracoProperties = new List() + }; + + var asDictionary = (IDictionary)d; + + var publishedVersion = default(Guid); + //some content items don't have a published version + if (asDictionary.ContainsKey("publishedVersion") && asDictionary["publishedVersion"] != null) + { + Guid.TryParse(d.publishedVersion.ToString(), out publishedVersion); + } + var newestVersion = default(Guid); + Guid.TryParse(d.newestVersion.ToString(), out newestVersion); + + entity.IsPublished = publishedVersion != default(Guid) || (newestVersion != default(Guid) && publishedVersion == newestVersion); + entity.IsDraft = newestVersion != default(Guid) && (publishedVersion == default(Guid) || publishedVersion != newestVersion); + entity.HasPendingChanges = (publishedVersion != default(Guid) && newestVersion != default(Guid)) && publishedVersion != newestVersion; + + return entity; + } + public UmbracoEntity BuildEntity(EntityRepository.UmbracoEntityDto dto) { var entity = new UmbracoEntity(dto.Trashed) diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs index 8651068030..bbfea4082a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs @@ -1,12 +1,18 @@ using System; using System.Collections.Generic; +using System.Dynamic; +using System.Globalization; using System.Linq; +using System.Reflection; +using System.Text; using Umbraco.Core.Models; +using Umbraco.Core; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Strings; namespace Umbraco.Core.Persistence.Repositories { @@ -124,15 +130,43 @@ namespace Umbraco.Core.Persistence.Repositories var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate().Append(GetGroupBy(isContent, isMedia)); - var dtos = isMedia - ? _work.Database.Fetch( - new UmbracoEntityRelator().Map, sql) - : _work.Database.Fetch(sql); - var factory = new UmbracoEntityFactory(); - var list = dtos.Select(factory.BuildEntity).Cast().ToList(); - return list; + if (isMedia) + { + //treat media differently for now + var dtos = _work.Database.Fetch( + new UmbracoEntityRelator().Map, sql); + return dtos.Select(factory.BuildEntity).Cast().ToArray(); + } + else + { + //use dynamic so that we can get ALL properties from the SQL + //we'll have to stitch stuff together manually but we can get our + //additional data to put in the dictionary. + var dtos = _work.Database.Fetch(sql); + var entityProps = TypeHelper.GetPublicProperties(typeof (IUmbracoEntity)).Select(x => x.Name).ToArray(); + var result = new List(); + foreach (var d in dtos) + { + //build the initial entity + IUmbracoEntity entity = factory.BuildEntityFromDynamic(d); + + //convert the dynamic row to dictionary + var asDictionary = (IDictionary) d; + + //figure out what extra properties we have that are not on the IUmbracoEntity and add them to additional data + foreach (var k in asDictionary.Keys + .Select(x => new {orig = x, title = x.ConvertCase(StringAliasCaseType.PascalCase)}) + .Where(x => entityProps.InvariantContains(x.title) == false)) + { + entity.AdditionalData[k.title] = asDictionary[k.orig]; + } + + result.Add(entity); + } + return result; + } } #endregion @@ -159,11 +193,12 @@ namespace Umbraco.Core.Persistence.Repositories if (isContent || isMedia) { - columns.Add("published.versionId as publishedVerison"); + columns.Add("published.versionId as publishedVersion"); columns.Add("latest.versionId as newestVersion"); columns.Add("contenttype.alias"); columns.Add("contenttype.icon"); columns.Add("contenttype.thumbnail"); + columns.Add("contenttype.isContainer"); } if (isMedia) @@ -251,6 +286,7 @@ namespace Umbraco.Core.Persistence.Repositories columns.Add("contenttype.alias"); columns.Add("contenttype.icon"); columns.Add("contenttype.thumbnail"); + columns.Add("contenttype.isContainer"); } if (isMedia) @@ -287,7 +323,7 @@ namespace Umbraco.Core.Persistence.Repositories [Column("children")] public int Children { get; set; } - [Column("publishedVerison")] + [Column("publishedVersion")] public Guid PublishedVersion { get; set; } [Column("newestVersion")] diff --git a/src/Umbraco.Core/TypeHelper.cs b/src/Umbraco.Core/TypeHelper.cs index adda1d3298..efb525e575 100644 --- a/src/Umbraco.Core/TypeHelper.cs +++ b/src/Umbraco.Core/TypeHelper.cs @@ -203,6 +203,53 @@ namespace Umbraco.Core }); } + /// + /// Returns all public properties including inherited properties even for interfaces + /// + /// + /// + /// + /// taken from http://stackoverflow.com/questions/358835/getproperties-to-return-all-properties-for-an-interface-inheritance-hierarchy + /// + public static PropertyInfo[] GetPublicProperties(Type type) + { + if (type.IsInterface) + { + var propertyInfos = new List(); + + var considered = new List(); + var queue = new Queue(); + considered.Add(type); + queue.Enqueue(type); + while (queue.Count > 0) + { + var subType = queue.Dequeue(); + foreach (var subInterface in subType.GetInterfaces()) + { + if (considered.Contains(subInterface)) continue; + + considered.Add(subInterface); + queue.Enqueue(subInterface); + } + + var typeProperties = subType.GetProperties( + BindingFlags.FlattenHierarchy + | BindingFlags.Public + | BindingFlags.Instance); + + var newPropertyInfos = typeProperties + .Where(x => !propertyInfos.Contains(x)); + + propertyInfos.InsertRange(0, newPropertyInfos); + } + + return propertyInfos.ToArray(); + } + + return type.GetProperties(BindingFlags.FlattenHierarchy + | BindingFlags.Public | BindingFlags.Instance); + } + /// /// Gets (and caches) discoverable in the current for a given . /// From 27e6e9b91c170a6722baabd181e18f4558da6531 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 9 Oct 2013 13:52:44 +1100 Subject: [PATCH 05/11] removes duplicate declaration --- src/Umbraco.Core/Models/DataTypeDefinition.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Core/Models/DataTypeDefinition.cs b/src/Umbraco.Core/Models/DataTypeDefinition.cs index 387101f5d8..00966496f3 100644 --- a/src/Umbraco.Core/Models/DataTypeDefinition.cs +++ b/src/Umbraco.Core/Models/DataTypeDefinition.cs @@ -34,7 +34,6 @@ namespace Umbraco.Core.Models _parentId = parentId; _controlId = controlId; _additionalData = new Dictionary(); - _additionalData = new Dictionary(); } private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); From 80488e012fc822e2a9981434c40d95aebd84edf0 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Oct 2013 14:00:35 +1100 Subject: [PATCH 06/11] Fixes up and implements more if the IMemberService, repository and membership providers (both legacy and what seems to be the new one). --- src/Umbraco.Core/Constants-Conventions.cs | 4 ++ src/Umbraco.Core/Models/PagedResult.cs | 63 +++++++++++++++++++ .../Persistence/PetaPocoExtensions.cs | 14 +++++ .../Interfaces/IMemberRepository.cs | 2 + .../Repositories/MemberRepository.cs | 22 +++++++ src/Umbraco.Core/Properties/AssemblyInfo.cs | 3 +- src/Umbraco.Core/Services/IMemberService.cs | 2 + src/Umbraco.Core/Services/MemberService.cs | 8 +++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../Providers/MembersMembershipProvider.cs | 13 +++- .../businesslogic/member/Member.cs | 4 +- .../members/MembersMembershipProvider.cs | 24 +++++-- .../umbraco.providers.csproj | 4 ++ src/umbraco.sln | 7 ++- 14 files changed, 160 insertions(+), 11 deletions(-) create mode 100644 src/Umbraco.Core/Models/PagedResult.cs diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index b8161b9480..81cec10f94 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -99,6 +99,10 @@ namespace Umbraco.Core /// public static class Member { + public static readonly string UmbracoMemberProviderName = "UmbracoMembershipProvider"; + + public static readonly string UmbracoRoleProviderName = "UmbracoRoleProvider"; + /// /// Property alias for a Members Password Question /// diff --git a/src/Umbraco.Core/Models/PagedResult.cs b/src/Umbraco.Core/Models/PagedResult.cs new file mode 100644 index 0000000000..2e35da9a96 --- /dev/null +++ b/src/Umbraco.Core/Models/PagedResult.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace Umbraco.Core.Models +{ + /// + /// Represents a paged result for a model collection + /// + /// + [DataContract(Name = "pagedCollection", Namespace = "")] + public class PagedResult + { + public PagedResult(long totalItems, long pageNumber, long pageSize) + { + TotalItems = totalItems; + PageNumber = pageNumber; + PageSize = pageSize; + + if (pageSize > 0) + { + TotalPages = (long) Math.Ceiling(totalItems/(Decimal) pageSize); + } + else + { + TotalPages = 1; + } + } + + [DataMember(Name = "pageNumber")] + public long PageNumber { get; private set; } + + [DataMember(Name = "pageSize")] + public long PageSize { get; private set; } + + [DataMember(Name = "totalPages")] + public long TotalPages { get; private set; } + + [DataMember(Name = "totalItems")] + public long TotalItems { get; private set; } + + [DataMember(Name = "items")] + public IEnumerable Items { get; set; } + + /// + /// Calculates the skip size based on the paged parameters specified + /// + /// + /// Returns 0 if the page number or page size is zero + /// + internal int SkipSize + { + get + { + if (PageNumber > 0 && PageSize > 0) + { + return Convert.ToInt32((PageNumber - 1)*PageSize); + } + return 0; + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs index eb10bf7f87..f249ca4577 100644 --- a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs +++ b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Data; using System.Linq; +using System.Text.RegularExpressions; using Umbraco.Core.Logging; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.DatabaseModelDefinitions; @@ -17,6 +18,19 @@ namespace Umbraco.Core.Persistence internal static event CreateTableEventHandler NewTable; + /// + /// This will escape single @ symbols for peta poco values so it doesn't think it's a parameter + /// + /// + /// + /// + public static string EscapeAtSymbols(this Database db, string value) + { + //this fancy regex will only match a single @ not a double, etc... + var regex = new Regex("(?(this Database db) where T : new() { diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs index be959da07c..c0df447dfa 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs @@ -11,5 +11,7 @@ namespace Umbraco.Core.Persistence.Repositories /// /// IEnumerable GetByMemberGroup(string groupName); + + IEnumerable GetMembersByEmails(params string[] emails); } } \ 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 caa92f94ef..bd3bba2ccd 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -404,6 +404,28 @@ namespace Umbraco.Core.Persistence.Repositories return BuildFromDtos(dtos); } + public IEnumerable GetMembersByEmails(params string[] emails) + { + var sql = GetBaseQuery(false); + if (emails.Any()) + { + var statement = string.Join(" OR ", + emails.Select(x => + string.Format( + "cmsMember.Email='{0}'", + //we have to escape the @ symbol for petapoco to work!! with 2 @@ symbols + Database.EscapeAtSymbols(x)))); + sql.Where(statement); + } + sql.OrderByDescending(x => x.VersionDate); + + var dtos = + Database.Fetch( + new PropertyDataRelator().Map, sql); + + return BuildFromDtos(dtos); + } + private IMember BuildFromDto(List dtos) { if (dtos == null || dtos.Any() == false) diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs index f58744b9f5..641fde43a1 100644 --- a/src/Umbraco.Core/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs @@ -43,4 +43,5 @@ using System.Security.Permissions; [assembly: InternalsVisibleTo("Concorde.Sync")] [assembly: InternalsVisibleTo("Umbraco.Belle")] -[assembly: InternalsVisibleTo("Umbraco.VisualStudio")] \ No newline at end of file +[assembly: InternalsVisibleTo("Umbraco.VisualStudio")] +[assembly: InternalsVisibleTo("umbraco.providers")] \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IMemberService.cs b/src/Umbraco.Core/Services/IMemberService.cs index a6a2a14424..343f1b47b5 100644 --- a/src/Umbraco.Core/Services/IMemberService.cs +++ b/src/Umbraco.Core/Services/IMemberService.cs @@ -35,5 +35,7 @@ namespace Umbraco.Core.Services void Delete(IMember membershipUser); void Save(IMember membershipUser); + + IEnumerable GetMembersByEmails(params string[] emails); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/MemberService.cs b/src/Umbraco.Core/Services/MemberService.cs index 914ef70e60..21cce31400 100644 --- a/src/Umbraco.Core/Services/MemberService.cs +++ b/src/Umbraco.Core/Services/MemberService.cs @@ -108,6 +108,14 @@ namespace Umbraco.Core.Services } } + public IEnumerable GetMembersByEmails(params string[] emails) + { + using (var repository = _repositoryFactory.CreateMemberRepository(_uowProvider.GetUnitOfWork())) + { + return repository.GetMembersByEmails(emails); + } + } + /// /// Gets a list of Members with a certain string property value /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 5db9dd7d56..ff48c7bcc5 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -209,6 +209,7 @@ + diff --git a/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs b/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs index b02ed4ecf2..0cc4146f85 100644 --- a/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs +++ b/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Specialized; using System.Configuration.Provider; +using System.Linq; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; @@ -8,6 +9,7 @@ using System.Web.Hosting; using System.Web.Security; using Umbraco.Core; using Umbraco.Core.Logging; +using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Core.Models.Membership; @@ -687,7 +689,16 @@ namespace Umbraco.Web.Security.Providers /// public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) { - throw new System.NotImplementedException(); + var byEmail = MemberService.GetMembersByEmails(emailToMatch).ToArray(); + totalRecords = byEmail.Length; + var pagedResult = new PagedResult(totalRecords, pageIndex, pageSize); + + var collection = new MembershipUserCollection(); + foreach (var m in byEmail.Skip(pagedResult.SkipSize).Take(pageSize)) + { + collection.Add(m.AsConcreteMembershipUser()); + } + return collection; } #region Private methods diff --git a/src/umbraco.cms/businesslogic/member/Member.cs b/src/umbraco.cms/businesslogic/member/Member.cs index 947330b292..b6c6a9be18 100644 --- a/src/umbraco.cms/businesslogic/member/Member.cs +++ b/src/umbraco.cms/businesslogic/member/Member.cs @@ -30,8 +30,8 @@ namespace umbraco.cms.businesslogic.member public class Member : Content { #region Constants and static members - public static readonly string UmbracoMemberProviderName = "UmbracoMembershipProvider"; - public static readonly string UmbracoRoleProviderName = "UmbracoRoleProvider"; + public static readonly string UmbracoMemberProviderName = Constants.Conventions.Member.UmbracoMemberProviderName; + public static readonly string UmbracoRoleProviderName = Constants.Conventions.Member.UmbracoRoleProviderName; public static readonly Guid _objectType = new Guid(Constants.ObjectTypes.Member); private static readonly object m_Locker = new object(); diff --git a/src/umbraco.providers/members/MembersMembershipProvider.cs b/src/umbraco.providers/members/MembersMembershipProvider.cs index 0572613d78..7c8fe8e63a 100644 --- a/src/umbraco.providers/members/MembersMembershipProvider.cs +++ b/src/umbraco.providers/members/MembersMembershipProvider.cs @@ -1,20 +1,26 @@ #region namespace using System; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Web.Security; using System.Configuration; +using Umbraco.Core; +using Umbraco.Core.Models; using umbraco.BusinessLogic; using System.Security.Cryptography; using System.Web.Util; using System.Collections.Specialized; using System.Configuration.Provider; using umbraco.cms.businesslogic; -using umbraco.cms.businesslogic.member; - using System.Security; using System.Security.Permissions; using System.Runtime.CompilerServices; +using Member = umbraco.cms.businesslogic.member.Member; +using MemberType = umbraco.cms.businesslogic.member.MemberType; +using Umbraco.Core.Models.Membership; +using User = umbraco.BusinessLogic.User; + #endregion namespace umbraco.providers.members @@ -439,7 +445,16 @@ namespace umbraco.providers.members /// public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) { - throw new Exception("The method or operation is not implemented."); + var byEmail = ApplicationContext.Current.Services.MemberService.GetMembersByEmails(emailToMatch).ToArray(); + totalRecords = byEmail.Length; + var pagedResult = new PagedResult(totalRecords, pageIndex, pageSize); + + var collection = new MembershipUserCollection(); + foreach (var m in byEmail.Skip(pagedResult.SkipSize).Take(pageSize)) + { + collection.Add(m.AsConcreteMembershipUser()); + } + return collection; } /// @@ -751,7 +766,7 @@ namespace umbraco.providers.members return null; } - + /// /// Verifies that the specified user name and password exist in the data source. /// @@ -973,6 +988,7 @@ namespace umbraco.providers.members DateTime.Now, DateTime.Now, DateTime.Now); } } + #endregion } } diff --git a/src/umbraco.providers/umbraco.providers.csproj b/src/umbraco.providers/umbraco.providers.csproj index 29e8df7ccc..7c6f3892eb 100644 --- a/src/umbraco.providers/umbraco.providers.csproj +++ b/src/umbraco.providers/umbraco.providers.csproj @@ -97,6 +97,10 @@ {ccd75ec3-63db-4184-b49d-51c1dd337230} umbraco.cms + + {31785bc3-256c-4613-b2f5-a1b0bdded8c1} + Umbraco.Core + {C7CB79F0-1C97-4B33-BFA7-00731B579AE2} umbraco.datalayer diff --git a/src/umbraco.sln b/src/umbraco.sln index 2e6c017bee..0b7902d964 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -1,7 +1,5 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.20827.3 -MinimumVisualStudioVersion = 10.0.40219.1 +# Visual Studio 2012 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{2849E9D4-3B4E-40A3-A309-F3CB4F0E125F}" ProjectSection(SolutionItems) = preProject ..\build\Build.bat = ..\build\Build.bat @@ -159,4 +157,7 @@ Global {73529637-28F5-419C-A6BB-D094E39DE614} = {DD32977B-EF54-475B-9A1B-B97A502C6E58} {B555AAE6-0F56-442F-AC9F-EF497DB38DE7} = {DD32977B-EF54-475B-9A1B-B97A502C6E58} EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection EndGlobal From a3bca4211770b42c244f613ed9d5a9d9a77e6e37 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Oct 2013 14:10:28 +1100 Subject: [PATCH 07/11] updates UmbracoMembershipMember --- .../Membership/UmbracoMembershipMember.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs b/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs index 06e50ecd9b..d55cc77f66 100644 --- a/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs +++ b/src/Umbraco.Core/Models/Membership/UmbracoMembershipMember.cs @@ -2,6 +2,8 @@ namespace Umbraco.Core.Models.Membership { + //TODO: THere's still a bunch of properties that don't exist in this use that need to be mapped somehow. + internal class UmbracoMembershipMember : MembershipUser { private readonly IMember _member; @@ -21,5 +23,44 @@ namespace Umbraco.Core.Models.Membership get { return _member.Email; } set { _member.Email = value; } } + + public override object ProviderUserKey + { + get { return _member.Key; } + } + + public override System.DateTime CreationDate + { + get { return _member.CreateDate; } + } + + public override string UserName + { + get { return _member.Username; } + } + + public override string Comment + { + get { return _member.Comments; } + set { _member.Comments = value; } + } + + public override bool IsApproved + { + get { return _member.IsApproved; } + set { _member.IsApproved = value; } + } + + public override bool IsLockedOut + { + get { return _member.IsLockedOut; } + } + + public override System.DateTime LastLoginDate + { + get { return _member.LastLoginDate; } + set { _member.LastLoginDate = value; } + } + } } \ No newline at end of file From 4170713e29acf4040dd81bc3329036e8d41c5442 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Oct 2013 14:51:01 +1100 Subject: [PATCH 08/11] More member service and provider updates Conflicts: src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs --- .../Interfaces/IMemberRepository.cs | 1 - .../Repositories/MemberRepository.cs | 22 ------------------- src/Umbraco.Core/Services/IMemberService.cs | 2 +- src/Umbraco.Core/Services/MemberService.cs | 17 +++++++++++--- .../Providers/MembersMembershipProvider.cs | 2 +- .../members/MembersMembershipProvider.cs | 2 +- 6 files changed, 17 insertions(+), 29 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs index c0df447dfa..55f3099640 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs @@ -12,6 +12,5 @@ namespace Umbraco.Core.Persistence.Repositories /// IEnumerable GetByMemberGroup(string groupName); - IEnumerable GetMembersByEmails(params string[] emails); } } \ 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 bd3bba2ccd..caa92f94ef 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -404,28 +404,6 @@ namespace Umbraco.Core.Persistence.Repositories return BuildFromDtos(dtos); } - public IEnumerable GetMembersByEmails(params string[] emails) - { - var sql = GetBaseQuery(false); - if (emails.Any()) - { - var statement = string.Join(" OR ", - emails.Select(x => - string.Format( - "cmsMember.Email='{0}'", - //we have to escape the @ symbol for petapoco to work!! with 2 @@ symbols - Database.EscapeAtSymbols(x)))); - sql.Where(statement); - } - sql.OrderByDescending(x => x.VersionDate); - - var dtos = - Database.Fetch( - new PropertyDataRelator().Map, sql); - - return BuildFromDtos(dtos); - } - private IMember BuildFromDto(List dtos) { if (dtos == null || dtos.Any() == false) diff --git a/src/Umbraco.Core/Services/IMemberService.cs b/src/Umbraco.Core/Services/IMemberService.cs index 343f1b47b5..8894c419fc 100644 --- a/src/Umbraco.Core/Services/IMemberService.cs +++ b/src/Umbraco.Core/Services/IMemberService.cs @@ -36,6 +36,6 @@ namespace Umbraco.Core.Services void Save(IMember membershipUser); - IEnumerable GetMembersByEmails(params string[] emails); + IEnumerable FindMembersByEmail(string emailStringToMatch); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/MemberService.cs b/src/Umbraco.Core/Services/MemberService.cs index 21cce31400..87eee84863 100644 --- a/src/Umbraco.Core/Services/MemberService.cs +++ b/src/Umbraco.Core/Services/MemberService.cs @@ -108,11 +108,22 @@ namespace Umbraco.Core.Services } } - public IEnumerable GetMembersByEmails(params string[] emails) + /// + /// Does a search for members that contain the specified string in their email address + /// + /// + /// + public IEnumerable FindMembersByEmail(string emailStringToMatch) { - using (var repository = _repositoryFactory.CreateMemberRepository(_uowProvider.GetUnitOfWork())) + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateMemberRepository(uow)) { - return repository.GetMembersByEmails(emails); + var query = new Query(); + + + query.Where(member => member.Email.Contains(emailStringToMatch)); + + return repository.GetByQuery(query); } } diff --git a/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs b/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs index 0cc4146f85..1f83d7a686 100644 --- a/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs +++ b/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs @@ -689,7 +689,7 @@ namespace Umbraco.Web.Security.Providers /// public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) { - var byEmail = MemberService.GetMembersByEmails(emailToMatch).ToArray(); + var byEmail = MemberService.FindMembersByEmail(emailToMatch).ToArray(); totalRecords = byEmail.Length; var pagedResult = new PagedResult(totalRecords, pageIndex, pageSize); diff --git a/src/umbraco.providers/members/MembersMembershipProvider.cs b/src/umbraco.providers/members/MembersMembershipProvider.cs index 7c8fe8e63a..d1da270942 100644 --- a/src/umbraco.providers/members/MembersMembershipProvider.cs +++ b/src/umbraco.providers/members/MembersMembershipProvider.cs @@ -445,7 +445,7 @@ namespace umbraco.providers.members /// public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) { - var byEmail = ApplicationContext.Current.Services.MemberService.GetMembersByEmails(emailToMatch).ToArray(); + var byEmail = ApplicationContext.Current.Services.MemberService.FindMembersByEmail(emailToMatch).ToArray(); totalRecords = byEmail.Length; var pagedResult = new PagedResult(totalRecords, pageIndex, pageSize); From 6c77749e32d9a8a6f0296953cca33acda24b68cf Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Oct 2013 15:45:52 +1100 Subject: [PATCH 09/11] Fixes: U4-3077 No id "xxxx" exists in dictionary --- src/umbraco.cms/businesslogic/Dictionary.cs | 47 +++++++++++---------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/umbraco.cms/businesslogic/Dictionary.cs b/src/umbraco.cms/businesslogic/Dictionary.cs index 7a655f8373..e524f90429 100644 --- a/src/umbraco.cms/businesslogic/Dictionary.cs +++ b/src/umbraco.cms/businesslogic/Dictionary.cs @@ -2,8 +2,10 @@ using System; using System.Collections; using System.Collections.Concurrent; using System.Data; +using System.Threading; using System.Xml; using System.Linq; +using Umbraco.Core; using umbraco.cms.businesslogic.language; using umbraco.DataLayer; using umbraco.BusinessLogic; @@ -19,7 +21,7 @@ namespace umbraco.cms.businesslogic public class Dictionary { private static volatile bool _cacheIsEnsured = false; - private static readonly object Locker = new object(); + private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(); private static readonly ConcurrentDictionary DictionaryItems = new ConcurrentDictionary(); private static readonly Guid TopLevelParent = new Guid("41c7638d-f529-4bff-853e-59a0c2fb1bde"); @@ -33,30 +35,28 @@ namespace umbraco.cms.businesslogic /// private static void EnsureCache() { - if (!_cacheIsEnsured) + using (var lck = new UpgradeableReadLock(Locker)) { - lock (Locker) + if (_cacheIsEnsured) return; + + lck.UpgradeToWriteLock(); + + using (var dr = SqlHelper.ExecuteReader("Select pk, id, [key], parent from cmsDictionary")) { - if (!_cacheIsEnsured) + while (dr.Read()) { - using (IRecordsReader dr = SqlHelper.ExecuteReader("Select pk, id, [key], parent from cmsDictionary")) - { - while (dr.Read()) - { - //create new dictionaryitem object and put in cache - var item = new DictionaryItem(dr.GetInt("pk"), - dr.GetString("key"), - dr.GetGuid("id"), - dr.GetGuid("parent")); + //create new dictionaryitem object and put in cache + var item = new DictionaryItem(dr.GetInt("pk"), + dr.GetString("key"), + dr.GetGuid("id"), + dr.GetGuid("parent")); - DictionaryItems.TryAdd(item.key, item); - } - } - - _cacheIsEnsured = true; + DictionaryItems.TryAdd(item.key, item); } } - } + + _cacheIsEnsured = true; + } } /// @@ -64,9 +64,12 @@ namespace umbraco.cms.businesslogic /// internal static void ClearCache() { - DictionaryItems.Clear(); - //ensure the flag is reset so that EnsureCache will re-cache everything - _cacheIsEnsured = false; + using (new WriteLock(Locker)) + { + DictionaryItems.Clear(); + //ensure the flag is reset so that EnsureCache will re-cache everything + _cacheIsEnsured = false; + } } /// From cb8c1a31cb52c41f7e3b61a50ebebdc3d603b294 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Oct 2013 16:50:57 +1100 Subject: [PATCH 10/11] Fixes/updates more of the member repository and related items Conflicts: src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs --- src/Umbraco.Core/Models/Member.cs | 2 +- .../Repositories/MemberRepository.cs | 12 ++++++++-- .../Providers/MembersMembershipProvider.cs | 22 ++++++++++--------- .../businesslogic/member/Member.cs | 2 ++ .../members/MembersMembershipProvider.cs | 8 ++++++- 5 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Core/Models/Member.cs b/src/Umbraco.Core/Models/Member.cs index 335beb9ee4..5fd48da994 100644 --- a/src/Umbraco.Core/Models/Member.cs +++ b/src/Umbraco.Core/Models/Member.cs @@ -427,7 +427,7 @@ namespace Umbraco.Core.Models public override void ChangeTrashedState(bool isTrashed, int parentId = -20) { - throw new NotImplementedException("Members can't be trashed as no Recycle Bin exists, so use of this method is invalid"); + throw new NotSupportedException("Members can't be trashed as no Recycle Bin exists, so use of this method is invalid"); } /* Internal experiment - only used for mapping queries. diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs index caa92f94ef..b8024939b0 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -298,11 +298,19 @@ namespace Umbraco.Core.Persistence.Repositories //TODO ContentType for the Member entity //Create the PropertyData for this version - cmsPropertyData - var propertyFactory = new PropertyFactory(entity.ContentType, entity.Version, entity.Id); - var propertyDataDtos = propertyFactory.BuildDto(((Member)entity).Properties); + var propertyFactory = new PropertyFactory(entity.ContentType, entity.Version, entity.Id); var keyDictionary = new Dictionary(); //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(); + + var propertyDataDtos = propertyFactory.BuildDto(existingProperties); + foreach (var propertyDataDto in propertyDataDtos) { if (propertyDataDto.Id > 0) diff --git a/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs b/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs index 1f83d7a686..c4b493c166 100644 --- a/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs +++ b/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Specialized; +using System.ComponentModel.DataAnnotations; using System.Configuration.Provider; using System.Linq; using System.Security.Cryptography; @@ -332,6 +333,9 @@ namespace Umbraco.Web.Security.Providers member.PasswordQuestion = passwordQuestion; member.PasswordAnswer = passwordAnswer; + //encrypts/hashes the password depending on the settings + member.Password = EncryptOrHashPassword(member.Password); + MemberService.Save(member); status = MembershipCreateStatus.Success; @@ -362,7 +366,7 @@ namespace Umbraco.Web.Security.Providers } var member = MemberService.GetByUsername(username); - var encodedPassword = EncodePassword(password); + var encodedPassword = EncryptOrHashPassword(password); if (member.Password == encodedPassword) { @@ -429,12 +433,12 @@ namespace Umbraco.Web.Security.Providers var member = MemberService.GetByUsername(username); if (member == null) return false; - var encodedPassword = EncodePassword(oldPassword); + var encodedPassword = EncryptOrHashPassword(oldPassword); if (member.Password == encodedPassword) { - member.Password = EncodePassword(newPassword); + member.Password = EncryptOrHashPassword(newPassword); MemberService.Save(member); return true; @@ -466,7 +470,7 @@ namespace Umbraco.Web.Security.Providers if (_requiresQuestionAndAnswer == false || (_requiresQuestionAndAnswer && answer == member.PasswordAnswer)) { member.Password = - EncodePassword(Membership.GeneratePassword(_minRequiredPasswordLength, + EncryptOrHashPassword(Membership.GeneratePassword(_minRequiredPasswordLength, _minRequiredNonAlphanumericCharacters)); MemberService.Save(member); } @@ -507,7 +511,7 @@ namespace Umbraco.Web.Security.Providers if (member.IsLockedOut) throw new ProviderException("The member is locked out."); - var encodedPassword = EncodePassword(password); + var encodedPassword = EncryptOrHashPassword(password); var authenticated = (encodedPassword == member.Password); @@ -725,11 +729,9 @@ namespace Umbraco.Web.Security.Providers private bool IsEmaiValid(string email) { - const string pattern = @"^(?!\.)(""([^""\r\\]|\\[""\r\\])*""|" - + @"([-a-z0-9!#$%&'*+/=?^_`{|}~]|(? @@ -737,7 +739,7 @@ namespace Umbraco.Web.Security.Providers /// /// The password. /// The encoded password. - private string EncodePassword(string password) + private string EncryptOrHashPassword(string password) { var encodedPassword = password; switch (PasswordFormat) diff --git a/src/umbraco.cms/businesslogic/member/Member.cs b/src/umbraco.cms/businesslogic/member/Member.cs index b6c6a9be18..3a2389d35b 100644 --- a/src/umbraco.cms/businesslogic/member/Member.cs +++ b/src/umbraco.cms/businesslogic/member/Member.cs @@ -211,6 +211,8 @@ namespace umbraco.cms.businesslogic.member /// The new member public static Member MakeNew(string Name, string LoginName, string Email, MemberType mbt, User u) { + if (mbt == null) throw new ArgumentNullException("mbt"); + var loginName = (!String.IsNullOrEmpty(LoginName)) ? LoginName : Name; if (String.IsNullOrEmpty(loginName)) diff --git a/src/umbraco.providers/members/MembersMembershipProvider.cs b/src/umbraco.providers/members/MembersMembershipProvider.cs index d1da270942..60782e6456 100644 --- a/src/umbraco.providers/members/MembersMembershipProvider.cs +++ b/src/umbraco.providers/members/MembersMembershipProvider.cs @@ -383,7 +383,13 @@ namespace umbraco.providers.members status = MembershipCreateStatus.DuplicateEmail; else { - Member m = Member.MakeNew(username, email, MemberType.GetByAlias(m_DefaultMemberTypeAlias), User.GetUser(0)); + var memberType = MemberType.GetByAlias(m_DefaultMemberTypeAlias); + if (memberType == null) + { + throw new InvalidOperationException("Could not find a member type with alias " + m_DefaultMemberTypeAlias + ". Ensure your membership provider configuration is up to date and that the default member type exists."); + } + + Member m = Member.MakeNew(username, email, memberType, User.GetUser(0)); m.Password = password; MembershipUser mUser = From 63a4e00f527d7222666d9e737715b83bbb344e20 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Oct 2013 17:23:29 +1100 Subject: [PATCH 11/11] tweaks to membership providers so if we cast we can specify the member type. --- .../Providers/MembersMembershipProvider.cs | 37 +++++-- .../members/MembersMembershipProvider.cs | 104 +++++++++++------- 2 files changed, 92 insertions(+), 49 deletions(-) diff --git a/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs b/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs index c4b493c166..811fc6be52 100644 --- a/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs +++ b/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs @@ -257,8 +257,9 @@ namespace Umbraco.Web.Security.Providers } /// - /// Adds a new membership user to the data source. + /// Adds a new membership user to the data source with the specified member type /// + /// A specific member type to create the member for /// The user name for the new user. /// The password for the new user. /// The e-mail address for the new user. @@ -270,16 +271,16 @@ namespace Umbraco.Web.Security.Providers /// /// A object populated with the information for the newly created user. /// - public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, + public MembershipUser CreateUser(string memberType, string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) { - LogHelper.Debug("Member signup requested: username -> " + username + ". email -> " +email); + LogHelper.Debug("Member signup requested: username -> " + username + ". email -> " + email); // Validate password if (IsPasswordValid(password) == false) { - status = MembershipCreateStatus.InvalidPassword; - return null; + status = MembershipCreateStatus.InvalidPassword; + return null; } // Validate email @@ -327,21 +328,41 @@ namespace Umbraco.Web.Security.Providers return null; } - var member = MemberService.CreateMember(email, username, password, DefaultMemberTypeAlias); + var member = MemberService.CreateMember(email, username, password, memberType); member.IsApproved = isApproved; member.PasswordQuestion = passwordQuestion; member.PasswordAnswer = passwordAnswer; - + //encrypts/hashes the password depending on the settings member.Password = EncryptOrHashPassword(member.Password); MemberService.Save(member); - + status = MembershipCreateStatus.Success; return member.AsConcreteMembershipUser(); } + /// + /// Adds a new membership user to the data source. + /// + /// The user name for the new user. + /// The password for the new user. + /// The e-mail address for the new user. + /// The password question for the new user. + /// The password answer for the new user + /// Whether or not the new user is approved to be validated. + /// The unique identifier from the membership data source for the user. + /// A enumeration value indicating whether the user was created successfully. + /// + /// A object populated with the information for the newly created user. + /// + public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, + bool isApproved, object providerUserKey, out MembershipCreateStatus status) + { + return CreateUser(DefaultMemberTypeAlias, username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status); + } + /// /// Processes a request to update the password question and answer for a membership user. /// diff --git a/src/umbraco.providers/members/MembersMembershipProvider.cs b/src/umbraco.providers/members/MembersMembershipProvider.cs index 60782e6456..b9dc4872a7 100644 --- a/src/umbraco.providers/members/MembersMembershipProvider.cs +++ b/src/umbraco.providers/members/MembersMembershipProvider.cs @@ -360,6 +360,68 @@ namespace umbraco.providers.members } } + /// + /// Adds a new membership user to the data source. + /// + /// + /// The user name for the new user. + /// The password for the new user. + /// The e-mail address for the new user. + /// The password question for the new user. + /// The password answer for the new user + /// Whether or not the new user is approved to be validated. + /// The unique identifier from the membership data source for the user. + /// A enumeration value indicating whether the user was created successfully. + /// + /// A object populated with the information for the newly created user. + /// + public MembershipUser CreateUser(string memberTypeAlias, string username, string password, string email, string passwordQuestion, + string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) + { + if (Member.GetMemberFromLoginName(username) != null) + status = MembershipCreateStatus.DuplicateUserName; + else if (Member.GetMemberFromEmail(email) != null && RequiresUniqueEmail) + status = MembershipCreateStatus.DuplicateEmail; + else + { + var memberType = MemberType.GetByAlias(memberTypeAlias); + if (memberType == null) + { + throw new InvalidOperationException("Could not find a member type with alias " + memberTypeAlias + ". Ensure your membership provider configuration is up to date and that the default member type exists."); + } + + Member m = Member.MakeNew(username, email, memberType, User.GetUser(0)); + m.Password = password; + + MembershipUser mUser = + ConvertToMembershipUser(m); + + // custom fields + if (!String.IsNullOrEmpty(m_PasswordRetrievalQuestionPropertyTypeAlias)) + UpdateMemberProperty(m, m_PasswordRetrievalQuestionPropertyTypeAlias, passwordQuestion); + + if (!String.IsNullOrEmpty(m_PasswordRetrievalAnswerPropertyTypeAlias)) + UpdateMemberProperty(m, m_PasswordRetrievalAnswerPropertyTypeAlias, passwordAnswer); + + if (!String.IsNullOrEmpty(m_ApprovedPropertyTypeAlias)) + UpdateMemberProperty(m, m_ApprovedPropertyTypeAlias, isApproved); + + if (!String.IsNullOrEmpty(m_LastLoginPropertyTypeAlias)) + { + mUser.LastActivityDate = DateTime.Now; + UpdateMemberProperty(m, m_LastLoginPropertyTypeAlias, mUser.LastActivityDate); + } + + // save + m.Save(); + + status = MembershipCreateStatus.Success; + + return mUser; + } + return null; + } + /// /// Adds a new membership user to the data source. /// @@ -377,47 +439,7 @@ namespace umbraco.providers.members public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) { - if (Member.GetMemberFromLoginName(username) != null) - status = MembershipCreateStatus.DuplicateUserName; - else if (Member.GetMemberFromEmail(email) != null && RequiresUniqueEmail) - status = MembershipCreateStatus.DuplicateEmail; - else - { - var memberType = MemberType.GetByAlias(m_DefaultMemberTypeAlias); - if (memberType == null) - { - throw new InvalidOperationException("Could not find a member type with alias " + m_DefaultMemberTypeAlias + ". Ensure your membership provider configuration is up to date and that the default member type exists."); - } - - Member m = Member.MakeNew(username, email, memberType, User.GetUser(0)); - m.Password = password; - - MembershipUser mUser = - ConvertToMembershipUser(m); - - // custom fields - if (!String.IsNullOrEmpty(m_PasswordRetrievalQuestionPropertyTypeAlias)) - UpdateMemberProperty(m, m_PasswordRetrievalQuestionPropertyTypeAlias, passwordQuestion); - - if (!String.IsNullOrEmpty(m_PasswordRetrievalAnswerPropertyTypeAlias)) - UpdateMemberProperty(m, m_PasswordRetrievalAnswerPropertyTypeAlias, passwordAnswer); - - if (!String.IsNullOrEmpty(m_ApprovedPropertyTypeAlias)) - UpdateMemberProperty(m, m_ApprovedPropertyTypeAlias, isApproved); - - if (!String.IsNullOrEmpty(m_LastLoginPropertyTypeAlias)) { - mUser.LastActivityDate = DateTime.Now; - UpdateMemberProperty(m, m_LastLoginPropertyTypeAlias, mUser.LastActivityDate); - } - - // save - m.Save(); - - status = MembershipCreateStatus.Success; - - return mUser; - } - return null; + return CreateUser(m_DefaultMemberTypeAlias, username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status); } ///