From 27e6e9b91c170a6722baabd181e18f4558da6531 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 9 Oct 2013 13:52:44 +1100 Subject: [PATCH 1/8] 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 2/8] 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 3/8] 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 4/8] 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 5/8] 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 6/8] 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 7/8] 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); } /// From 13dcfbbd2b6c350f76077ad0645cf2c7d83c884b Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 11 Oct 2013 10:58:31 +1100 Subject: [PATCH 8/8] Fixes build - removes .net 4.5 reference of EmailAddressAttribute - we'll leave that in for v7 when targettng 4.5 --- .../Security/Providers/MembersMembershipProvider.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs b/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs index 811fc6be52..f423d95fbd 100644 --- a/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs +++ b/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs @@ -750,9 +750,11 @@ namespace Umbraco.Web.Security.Providers private bool IsEmaiValid(string email) { - var validator = new EmailAddressAttribute(); - - return validator.IsValid(email); + const string pattern = @"^(?!\.)(""([^""\r\\]|\\[""\r\\])*""|" + + @"([-a-z0-9!#$%&'*+/=?^_`{|}~]|(?