From 2d857a8635bfb74f799d1a6425baafe2360b6d1c Mon Sep 17 00:00:00 2001 From: Bo Kingo Damgaard Date: Wed, 26 Jun 2013 10:47:27 +0200 Subject: [PATCH 1/4] Bugfix for previous pullrequest "Duplicate e-mail addresses: U4-315, U4-2147, U4-304" There was a bug in the previous pullrequest that didn't allow a member to saved if the email wasn't changed. Furthermore duplicate emails are now prevented if requireUniqueEmail was false, but is switched to true (when saving a member) --- .../umbraco/members/EditMember.aspx.cs | 10 +++++++++- .../businesslogic/member/Member.cs | 19 ++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/members/EditMember.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/members/EditMember.aspx.cs index 21161c31ca..68412ec702 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/members/EditMember.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/members/EditMember.aspx.cs @@ -142,7 +142,15 @@ namespace umbraco.cms.presentation.members void MemberEmailExistCheck_ServerValidate(object source, ServerValidateEventArgs args) { - if (MemberEmail.Text != "" && Member.GetMemberFromEmail(MemberEmail.Text.ToLower()) != null && Membership.Providers[Member.UmbracoMemberProviderName].RequiresUniqueEmail) + var oldEmail = MemberEmail.Text.ToLower(); + var newEmail = _document.Email.ToLower(); + var requireUniqueEmail = Membership.Providers[Member.UmbracoMemberProviderName].RequiresUniqueEmail; + var howManyMembersWithEmail = Member.GetMembersFromEmail(newEmail).Length; + if (((oldEmail == newEmail && howManyMembersWithEmail > 1) || + (oldEmail != newEmail && howManyMembersWithEmail > 0)) + && requireUniqueEmail) + // If the value hasn't changed and there are more than 1 member with that email, then false + // If the value has changed and there are any member with that new email, then false args.IsValid = false; else args.IsValid = true; diff --git a/src/umbraco.cms/businesslogic/member/Member.cs b/src/umbraco.cms/businesslogic/member/Member.cs index b94a4ee7e7..d59eddb8de 100644 --- a/src/umbraco.cms/businesslogic/member/Member.cs +++ b/src/umbraco.cms/businesslogic/member/Member.cs @@ -228,7 +228,7 @@ namespace umbraco.cms.businesslogic.member throw new Exception(String.Format("Duplicate User name! A member with the user name {0} already exists", loginName)); // Lowercased to prevent duplicates - Email = Email.ToLowerInvariant(); + Email = Email.ToLower(); Guid newId = Guid.NewGuid(); //create the cms node first @@ -563,14 +563,23 @@ namespace umbraco.cms.businesslogic.member } set { - var m = Member.GetMemberFromEmail(value); - if (m != null && Membership.Providers[UmbracoMemberProviderName].RequiresUniqueEmail) + var oldEmail = Email; + var newEmail = value.ToLower(); + var requireUniqueEmail = Membership.Providers[UmbracoMemberProviderName].RequiresUniqueEmail; + var howManyMembersWithEmail = Member.GetMembersFromEmail(newEmail).Length; + if (((oldEmail == newEmail && howManyMembersWithEmail > 1) || + (oldEmail != newEmail && howManyMembersWithEmail > 0)) + && requireUniqueEmail) { - throw new Exception(String.Format("Duplicate Email! A member with the e-mail {0} already exists", value.ToLower())); + // If the value hasn't changed and there are more than 1 member with that email, then throw + // If the value has changed and there are any member with that new email, then throw + throw new Exception(String.Format("Duplicate Email! A member with the e-mail {0} already exists", newEmail)); } SqlHelper.ExecuteNonQuery( "update cmsMember set Email = @email where nodeId = @id", - SqlHelper.CreateParameter("@id", Id), SqlHelper.CreateParameter("@email", value.ToLower())); + SqlHelper.CreateParameter("@id", Id), SqlHelper.CreateParameter("@email", newEmail)); + // Set the backing field to new value + m_Email = newEmail; } } #endregion From 639cb17741324e318da9c72c2cee69b905d89a37 Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Wed, 26 Jun 2013 14:31:10 +0200 Subject: [PATCH 2/4] Fixes U4-2182 and U4-2167 --- src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs | 6 +++--- src/umbraco.cms/businesslogic/member/Member.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs index 8b1a88126a..ef71c020b7 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs @@ -66,9 +66,9 @@ namespace Umbraco.Core.Persistence.Factories if (property.HasIdentity) dto.Id = property.Id; - if (property.DataTypeDatabaseType == DataTypeDatabaseType.Integer && property.Value != null && string.IsNullOrWhiteSpace(property.Value.ToString()) == false) + if (property.DataTypeDatabaseType == DataTypeDatabaseType.Integer) { - if (property.Value is bool) + if (property.Value is bool || property.PropertyType.DataTypeId == new Guid("38b352c1-e9f8-4fd8-9324-9a2eab06d97a")) { int val = Convert.ToInt32(property.Value); dto.Integer = val; @@ -76,7 +76,7 @@ namespace Umbraco.Core.Persistence.Factories else { int val; - if (int.TryParse(property.Value.ToString(), out val)) + if ((property.Value != null && string.IsNullOrWhiteSpace(property.Value.ToString()) == false) && int.TryParse(property.Value.ToString(), out val)) { dto.Integer = val; } diff --git a/src/umbraco.cms/businesslogic/member/Member.cs b/src/umbraco.cms/businesslogic/member/Member.cs index d59eddb8de..c4a85f50b9 100644 --- a/src/umbraco.cms/businesslogic/member/Member.cs +++ b/src/umbraco.cms/businesslogic/member/Member.cs @@ -635,7 +635,7 @@ namespace umbraco.cms.businesslogic.member string dbType = property.PropertyType.DataTypeDefinition.DbType; if (dbType.Equals("Integer")) { - if (property.Value is bool) + if (property.Value is bool || property.PropertyType.DataTypeDefinition.DataType.Id == new Guid("38b352c1-e9f8-4fd8-9324-9a2eab06d97a")) { int val = Convert.ToInt32(property.Value); poco.Integer = val; From bbf06cd13d31a08c63d3167c4d0c51cbf516e129 Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Wed, 26 Jun 2013 14:31:36 +0200 Subject: [PATCH 3/4] Fixes U4-2234 --- .../Persistence/Repositories/ContentTypeBaseRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index 44254e803b..fcfbc803e0 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -189,7 +189,7 @@ namespace Umbraco.Core.Persistence.Repositories .Where(x => x.NodeObjectType == new Guid("C66BA18E-EAF3-4CFF-8A22-41B16D66A972")) .Where(x => x.ContentTypeId == entity.Id); - var contentDtos = Database.Fetch(sql); + var contentDtos = Database.Fetch(sql); //Loop through all tracked keys, which corresponds to the ContentTypes that has been removed from the composition foreach (var key in compositionBase.RemovedContentTypeKeyTracker) { From 41b8de09b7fc726896cb5cd4d2d50ce12d1eb8f9 Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Wed, 26 Jun 2013 15:49:00 +0200 Subject: [PATCH 4/4] Fixes U4-2234 including test case from issue --- .../Models/ContentTypeCompositionBase.cs | 6 +- .../Services/ContentTypeServiceTests.cs | 134 ++++++++++++++++++ 2 files changed, 138 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs index 02a5e7d8f7..46fe00e629 100644 --- a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs @@ -20,8 +20,10 @@ namespace Umbraco.Core.Models { } - protected ContentTypeCompositionBase(IContentTypeBase parent) : base(parent) + protected ContentTypeCompositionBase(IContentTypeComposition parent) + : base(parent) { + AddContentType(parent); } private static readonly PropertyInfo ContentTypeCompositionSelector = @@ -73,7 +75,7 @@ namespace Umbraco.Core.Models if (contentType.ContentTypeComposition.Any(x => x.CompositionAliases().Any(ContentTypeCompositionExists))) return false; - if (!ContentTypeCompositionExists(contentType.Alias)) + if (ContentTypeCompositionExists(contentType.Alias) == false) { _contentTypeComposition.Add(contentType); OnPropertyChanged(ContentTypeCompositionSelector); diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs index ff322a2112..5e4842608a 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs @@ -71,6 +71,140 @@ namespace Umbraco.Tests.Services Assert.That(addedContentType, Is.True); } + [Test] + public void Can_Remove_ContentType_Composition_From_ContentType() + { + //Test for U4-2234 + var cts = ServiceContext.ContentTypeService; + //Arrange + var component = CreateComponent(); + cts.Save(component); + var banner = CreateBannerComponent(component); + cts.Save(banner); + var site = CreateSite(); + cts.Save(site); + var homepage = CreateHomepage(site); + cts.Save(homepage); + + //Add banner to homepage + var added = homepage.AddContentType(banner); + cts.Save(homepage); + + //Assert composition + var bannerExists = homepage.ContentTypeCompositionExists(banner.Alias); + var bannerPropertyExists = homepage.CompositionPropertyTypes.Any(x => x.Alias.Equals("bannerName")); + Assert.That(added, Is.True); + Assert.That(bannerExists, Is.True); + Assert.That(bannerPropertyExists, Is.True); + Assert.That(homepage.CompositionPropertyTypes.Count(), Is.EqualTo(6)); + + //Remove banner from homepage + var removed = homepage.RemoveContentType(banner.Alias); + cts.Save(homepage); + + //Assert composition + var bannerStillExists = homepage.ContentTypeCompositionExists(banner.Alias); + var bannerPropertyStillExists = homepage.CompositionPropertyTypes.Any(x => x.Alias.Equals("bannerName")); + Assert.That(removed, Is.True); + Assert.That(bannerStillExists, Is.False); + Assert.That(bannerPropertyStillExists, Is.False); + Assert.That(homepage.CompositionPropertyTypes.Count(), Is.EqualTo(4)); + } + + private ContentType CreateComponent() + { + var component = new ContentType(-1) + { + Alias = "component", + Name = "Component", + Description = "ContentType used for Component grouping", + Icon = ".sprTreeDoc3", + Thumbnail = "doc.png", + SortOrder = 1, + CreatorId = 0, + Trashed = false + }; + + var contentCollection = new PropertyTypeCollection(); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "componentGroup", Name = "Component Group", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 }); + component.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Component", SortOrder = 1 }); + + return component; + } + + private ContentType CreateBannerComponent(ContentType parent) + { + var banner = new ContentType(parent) + { + Alias = "banner", + Name = "Banner Component", + Description = "ContentType used for Banner Component", + Icon = ".sprTreeDoc3", + Thumbnail = "doc.png", + SortOrder = 1, + CreatorId = 0, + Trashed = false + }; + + var propertyType = new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) + { + Alias = "bannerName", + Name = "Banner Name", + Description = "", + HelpText = "", + Mandatory = false, + SortOrder = 2, + DataTypeDefinitionId = -88 + }; + banner.AddPropertyType(propertyType, "Component"); + return banner; + } + + private ContentType CreateSite() + { + var site = new ContentType(-1) + { + Alias = "site", + Name = "Site", + Description = "ContentType used for Site inheritence", + Icon = ".sprTreeDoc3", + Thumbnail = "doc.png", + SortOrder = 2, + CreatorId = 0, + Trashed = false + }; + + var contentCollection = new PropertyTypeCollection(); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "hostname", Name = "Hostname", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 }); + site.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Site Settings", SortOrder = 1 }); + + return site; + } + + private ContentType CreateHomepage(ContentType parent) + { + var contentType = new ContentType(parent) + { + Alias = "homepage", + Name = "Homepage", + Description = "ContentType used for the Homepage", + Icon = ".sprTreeDoc3", + Thumbnail = "doc.png", + SortOrder = 1, + CreatorId = 0, + Trashed = false + }; + + var contentCollection = new PropertyTypeCollection(); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -87 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "author", Name = "Author", Description = "Name of the author", HelpText = "", Mandatory = false, SortOrder = 3, DataTypeDefinitionId = -88 }); + + contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); + + return contentType; + } + private IEnumerable CreateContentTypeHierarchy() { //create the master type