From 0f5f7d24e5ffbc7f72769a89912eb0aff7c9d532 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Wed, 3 Nov 2021 16:46:56 +0100 Subject: [PATCH 01/41] Use standard Member property group name/alias --- src/Umbraco.Core/Constants-Conventions.cs | 9 +++++++-- .../Migrations/Install/DatabaseDataCreator.cs | 2 +- .../Implement/ContentTypeCommonRepository.cs | 4 ++-- .../Repositories/Implement/MemberTypeRepository.cs | 4 ++-- src/Umbraco.Core/Services/Implement/MemberService.cs | 6 ++++-- .../Persistence/Repositories/MemberTypeRepositoryTest.cs | 2 +- 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index 37267a5e22..c93b3fd6a8 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -242,9 +242,14 @@ namespace Umbraco.Core public const string FailedPasswordAttemptsLabel = "Failed Password Attempts"; /// - /// Group name to put the membership properties on + /// The standard properties group alias for membership properties. /// - internal const string StandardPropertiesGroupName = "Membership"; + public const string StandardPropertiesGroupAlias = "membership"; + + /// + /// The standard properties group name for membership properties. + /// + public const string StandardPropertiesGroupName = "Membership"; public static Dictionary GetStandardPropertyTypeStubs() { diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs index f8629a5c71..bd7a96f6e7 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs @@ -230,7 +230,7 @@ namespace Umbraco.Core.Migrations.Install _database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 54, UniqueId = new Guid(Constants.PropertyTypeGroups.Article), ContentTypeNodeId = 1036, Text = "Article", Alias = "article", SortOrder = 1 }); _database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 55, UniqueId = new Guid(Constants.PropertyTypeGroups.VectorGraphics), ContentTypeNodeId = 1037, Text = "Vector Graphics", Alias = "vectorGraphics", SortOrder = 1 }); //membership property group - _database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 11, UniqueId = new Guid(Constants.PropertyTypeGroups.Membership), ContentTypeNodeId = 1044, Text = "Membership", Alias = "membership", SortOrder = 1 }); + _database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 11, UniqueId = new Guid(Constants.PropertyTypeGroups.Membership), ContentTypeNodeId = 1044, Text = Constants.Conventions.Member.StandardPropertiesGroupName, Alias = Constants.Conventions.Member.StandardPropertiesGroupAlias, SortOrder = 1 }); } private void CreatePropertyTypeData() diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs index af3b12f050..5aae950c9f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs @@ -250,12 +250,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement if (contentType is MemberType memberType) { // ensure that the group exists (ok if it already exists) - memberType.AddPropertyGroup(Constants.Conventions.Member.StandardPropertiesGroupName); + memberType.AddPropertyGroup(Constants.Conventions.Member.StandardPropertiesGroupAlias, Constants.Conventions.Member.StandardPropertiesGroupName); // ensure that property types exist (ok if they already exist) foreach (var (alias, propertyType) in builtinProperties) { - var added = memberType.AddPropertyType(propertyType, Constants.Conventions.Member.StandardPropertiesGroupName); + var added = memberType.AddPropertyType(propertyType, Constants.Conventions.Member.StandardPropertiesGroupAlias, Constants.Conventions.Member.StandardPropertiesGroupName); if (added) { diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberTypeRepository.cs index 6475732cc1..765b365736 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberTypeRepository.cs @@ -142,11 +142,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } //By Convention we add 9 standard PropertyTypes to an Umbraco MemberType - entity.AddPropertyGroup(Constants.Conventions.Member.StandardPropertiesGroupName); + entity.AddPropertyGroup(Constants.Conventions.Member.StandardPropertiesGroupAlias, Constants.Conventions.Member.StandardPropertiesGroupName); var standardPropertyTypes = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); foreach (var standardPropertyType in standardPropertyTypes) { - entity.AddPropertyType(standardPropertyType.Value, Constants.Conventions.Member.StandardPropertiesGroupName); + entity.AddPropertyType(standardPropertyType.Value, Constants.Conventions.Member.StandardPropertiesGroupAlias, Constants.Conventions.Member.StandardPropertiesGroupName); } EnsureExplicitDataTypeForBuiltInProperties(entity); diff --git a/src/Umbraco.Core/Services/Implement/MemberService.cs b/src/Umbraco.Core/Services/Implement/MemberService.cs index b26a6bef39..00b768a3dc 100644 --- a/src/Umbraco.Core/Services/Implement/MemberService.cs +++ b/src/Umbraco.Core/Services/Implement/MemberService.cs @@ -1201,8 +1201,10 @@ namespace Umbraco.Core.Services.Implement var memType = new MemberType(-1); var propGroup = new PropertyGroup(MemberType.SupportsPublishingConst) { - Name = "Membership", - Id = --identity + Alias = Constants.Conventions.Member.StandardPropertiesGroupAlias, + Name = Constants.Conventions.Member.StandardPropertiesGroupName, + Id = --identity, + Key = identity.ToGuid() }; propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Ntext, Constants.Conventions.Member.Comments) { diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs index 4b9f3096ce..759b8e7b50 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs @@ -326,7 +326,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.AreEqual(3, memberType.PropertyTypes.Count()); // add one stub property, others are still missing - memberType.AddPropertyType(stubs.First().Value, Constants.Conventions.Member.StandardPropertiesGroupName); + memberType.AddPropertyType(stubs.First().Value, Constants.Conventions.Member.StandardPropertiesGroupAlias, Constants.Conventions.Member.StandardPropertiesGroupName); // saving *new* member type adds the (missing) stub properties repository.Save(memberType); From cd6d027e5198ab1b5b9135be48d95eaa79767c38 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Wed, 3 Nov 2021 16:47:52 +0100 Subject: [PATCH 02/41] Mark standard Member group as inherited --- src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs index dcf8f80290..dae5578791 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs @@ -163,6 +163,12 @@ namespace Umbraco.Web.Models.Mapping { MapTypeToDisplayBase(source, target); + var standardGroup = target.Groups.FirstOrDefault(x => x.Alias == Constants.Conventions.Member.StandardPropertiesGroupAlias); + if (standardGroup != null) + { + standardGroup.Inherited = true; + } + //map the MemberCanEditProperty,MemberCanViewProperty,IsSensitiveData foreach (var propertyType in source.PropertyTypes) { From 86fb382f81d9066df30a3d31d911a8794c93395a Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Thu, 4 Nov 2021 15:45:42 +0100 Subject: [PATCH 03/41] Do not explicitly create standard member property group --- .../Repositories/Implement/ContentTypeCommonRepository.cs | 4 ---- .../Repositories/Implement/MemberTypeRepository.cs | 1 - 2 files changed, 5 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs index 5aae950c9f..89c7a3d152 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeCommonRepository.cs @@ -249,14 +249,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // ensure builtin properties if (contentType is MemberType memberType) { - // ensure that the group exists (ok if it already exists) - memberType.AddPropertyGroup(Constants.Conventions.Member.StandardPropertiesGroupAlias, Constants.Conventions.Member.StandardPropertiesGroupName); - // ensure that property types exist (ok if they already exist) foreach (var (alias, propertyType) in builtinProperties) { var added = memberType.AddPropertyType(propertyType, Constants.Conventions.Member.StandardPropertiesGroupAlias, Constants.Conventions.Member.StandardPropertiesGroupName); - if (added) { var access = new MemberTypePropertyProfileAccess(false, false, false); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberTypeRepository.cs index 765b365736..abce4c687f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberTypeRepository.cs @@ -142,7 +142,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } //By Convention we add 9 standard PropertyTypes to an Umbraco MemberType - entity.AddPropertyGroup(Constants.Conventions.Member.StandardPropertiesGroupAlias, Constants.Conventions.Member.StandardPropertiesGroupName); var standardPropertyTypes = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); foreach (var standardPropertyType in standardPropertyTypes) { From 5bda825fd145f9876fe42a5ad904d2cce2bf2457 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Fri, 5 Nov 2021 11:05:13 +0100 Subject: [PATCH 04/41] Add Member Content App to display membership properties --- .../services/umbdataformatter.service.js | 60 +++++++------------ .../member/umb-member-node-info.html | 12 ---- .../member/umb-membergroup-node-info.html | 1 + .../member/apps/content/content.controller.js | 10 ---- .../views/member/apps/content/content.html | 25 ++------ .../member/apps/membership/membership.html | 7 +++ .../MemberEditorContentAppFactory.cs | 34 +++++++++++ .../Models/ContentEditing/MemberDisplay.cs | 4 +- .../Mapping/ContentTypeMapDefinition.cs | 6 -- .../Models/Mapping/MemberMapDefinition.cs | 9 ++- .../Mapping/MemberTabsAndPropertiesMapper.cs | 58 +++--------------- .../Models/Mapping/TabsAndPropertiesMapper.cs | 41 +++---------- src/Umbraco.Web/Runtime/WebInitialComposer.cs | 3 +- src/Umbraco.Web/Umbraco.Web.csproj | 5 +- 14 files changed, 101 insertions(+), 174 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/member/apps/membership/membership.html create mode 100644 src/Umbraco.Web/ContentApps/MemberEditorContentAppFactory.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js index 7270ae5bbf..8cbed8a0d0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js @@ -269,51 +269,37 @@ /** formats the display model used to display the member to the model used to save the member */ formatMemberPostData: function (displayModel, action) { - //this is basically the same as for media but we need to explicitly add the username,email, password to the save model + //this is basically the same as for media but we need to explicitly add the username, email, password to the save model var saveModel = this.formatMediaPostData(displayModel, action); saveModel.key = displayModel.key; - var genericTab = _.find(displayModel.tabs, function (item) { - return item.id === 0; - }); - - //map the member login, email, password and groups - var propLogin = _.find(genericTab.properties, function (item) { - return item.alias === "_umb_login"; - }); - var propEmail = _.find(genericTab.properties, function (item) { - return item.alias === "_umb_email"; - }); - var propPass = _.find(genericTab.properties, function (item) { - return item.alias === "_umb_password"; - }); - var propGroups = _.find(genericTab.properties, function (item) { - return item.alias === "_umb_membergroup"; - }); - saveModel.email = propEmail.value.trim(); - saveModel.username = propLogin.value.trim(); - - saveModel.password = this.formatChangePasswordModel(propPass.value); - - var selectedGroups = []; - for (var n in propGroups.value) { - if (propGroups.value[n] === true) { - selectedGroups.push(n); + // Map membership properties + _.each(displayModel.membershipProperties, prop => { + switch (prop.alias) { + case '_umb_login': + saveModel.username = prop.value.trim(); + break; + case '_umb_email': + saveModel.email = prop.value.trim(); + break; + case '_umb_password': + saveModel.password = this.formatChangePasswordModel(prop.value); + break; + case '_umb_membergroup': + saveModel.memberGroups = _.keys(_.pick(prop.value, value => value === true)); + break; } - } - saveModel.memberGroups = selectedGroups; + }); - //turn the dictionary into an array of pairs + // Map custom member provider properties var memberProviderPropAliases = _.pairs(displayModel.fieldConfig); - _.each(displayModel.tabs, function (tab) { - _.each(tab.properties, function (prop) { - var foundAlias = _.find(memberProviderPropAliases, function (item) { - return prop.alias === item[1]; - }); + _.each(displayModel.tabs, tab => { + _.each(tab.properties, prop => { + var foundAlias = _.find(memberProviderPropAliases, item => prop.alias === item[1]); if (foundAlias) { - //we know the current property matches an alias, now we need to determine which membership provider property it was for + // we know the current property matches an alias, now we need to determine which membership provider property it was for // by looking at the key switch (foundAlias[0]) { case "umbracoMemberLockedOut": @@ -330,8 +316,6 @@ }); }); - - return saveModel; }, diff --git a/src/Umbraco.Web.UI.Client/src/views/components/member/umb-member-node-info.html b/src/Umbraco.Web.UI.Client/src/views/components/member/umb-member-node-info.html index 5162caf13c..7811c7728c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/member/umb-member-node-info.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/member/umb-member-node-info.html @@ -1,18 +1,6 @@
- - -
-
{{ group.label }}
-
-
- - - -
-
-
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/member/umb-membergroup-node-info.html b/src/Umbraco.Web.UI.Client/src/views/components/member/umb-membergroup-node-info.html index 1f10cf0599..dfa90d3bfa 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/member/umb-membergroup-node-info.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/member/umb-membergroup-node-info.html @@ -1,5 +1,6 @@
+ diff --git a/src/Umbraco.Web.UI.Client/src/views/member/apps/content/content.controller.js b/src/Umbraco.Web.UI.Client/src/views/member/apps/content/content.controller.js index 06c1f3bb63..95cbfec66f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/member/apps/content/content.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/member/apps/content/content.controller.js @@ -9,7 +9,6 @@ vm.activeTabAlias = null; vm.setActiveTab = setActiveTab; - vm.hideSystemProperties = hideSystemProperties; $scope.$watchCollection('content.tabs', (newValue) => { @@ -33,15 +32,6 @@ vm.tabs.forEach(tab => tab.active = false); tab.active = true; } - - function hideSystemProperties (property) { - // hide some specific, known properties by alias - if (property.alias === "_umb_id" || property.alias === "_umb_doctype") { - return false; - } - // hide all label properties with the alias prefix "umbracoMember" (e.g. "umbracoMemberFailedPasswordAttempts") - return property.view !== "readonlyvalue" || property.alias.startsWith('umbracoMember') === false; - } } angular.module("umbraco").controller("Umbraco.Editors.Member.Apps.ContentController", MemberAppContentController); diff --git a/src/Umbraco.Web.UI.Client/src/views/member/apps/content/content.html b/src/Umbraco.Web.UI.Client/src/views/member/apps/content/content.html index e11ccfcc38..7cf91949c7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/member/apps/content/content.html +++ b/src/Umbraco.Web.UI.Client/src/views/member/apps/content/content.html @@ -6,38 +6,21 @@ - - - - + + -
- +
{{ group.label }}
-
- - +
-
diff --git a/src/Umbraco.Web.UI.Client/src/views/member/apps/membership/membership.html b/src/Umbraco.Web.UI.Client/src/views/member/apps/membership/membership.html new file mode 100644 index 0000000000..d541bb2c56 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/member/apps/membership/membership.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/Umbraco.Web/ContentApps/MemberEditorContentAppFactory.cs b/src/Umbraco.Web/ContentApps/MemberEditorContentAppFactory.cs new file mode 100644 index 0000000000..931d972b12 --- /dev/null +++ b/src/Umbraco.Web/ContentApps/MemberEditorContentAppFactory.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Core.Models.ContentEditing; +using Umbraco.Core.Models.Membership; + +namespace Umbraco.Web.ContentApps +{ + internal class MemberEditorContentAppFactory : IContentAppFactory + { + // see note on ContentApp + internal const int Weight = +50; + + private ContentApp _memberApp; + + public ContentApp GetContentAppFor(object source, IEnumerable userGroups) + { + switch (source) + { + case IMember _: + return _memberApp ?? (_memberApp = new ContentApp + { + Alias = "umbMembership", + Name = "Member", + Icon = "icon-user", + View = "views/member/apps/membership/membership.html", + Weight = Weight + }); + + default: + return null; + } + } + } +} diff --git a/src/Umbraco.Web/Models/ContentEditing/MemberDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/MemberDisplay.cs index b3a606dad1..38990db006 100644 --- a/src/Umbraco.Web/Models/ContentEditing/MemberDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/MemberDisplay.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Runtime.Serialization; -using Umbraco.Core.Models; using Umbraco.Core.Models.ContentEditing; using Umbraco.Core.Models.Membership; @@ -41,5 +40,8 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "apps")] public IEnumerable ContentApps { get; set; } + + [DataMember(Name = "membershipProperties")] + public IEnumerable MembershipProperties { get; set; } } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs index dae5578791..dcf8f80290 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs @@ -163,12 +163,6 @@ namespace Umbraco.Web.Models.Mapping { MapTypeToDisplayBase(source, target); - var standardGroup = target.Groups.FirstOrDefault(x => x.Alias == Constants.Conventions.Member.StandardPropertiesGroupAlias); - if (standardGroup != null) - { - standardGroup.Inherited = true; - } - //map the MemberCanEditProperty,MemberCanViewProperty,IsSensitiveData foreach (var propertyType in source.PropertyTypes) { diff --git a/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs index dcbe6b7534..6c807f16f1 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs @@ -82,12 +82,9 @@ namespace Umbraco.Web.Models.Mapping target.ContentTypeAlias = source.ContentType.Alias; target.ContentTypeName = source.ContentType.Name; target.CreateDate = source.CreateDate; - target.Email = source.Email; target.Icon = source.ContentType.Icon; target.Id = source.Id; target.Key = source.Key; - target.MemberProviderFieldMapping = GetMemberProviderFieldMapping(); - target.MembershipScenario = GetMembershipScenario(); target.Name = source.Name; target.Owner = _commonMapper.GetOwner(source, context); target.ParentId = source.ParentId; @@ -98,7 +95,13 @@ namespace Umbraco.Web.Models.Mapping target.TreeNodeUrl = _commonMapper.GetMemberTreeNodeUrl(source); target.Udi = Udi.Create(Constants.UdiEntityType.Member, source.Key); target.UpdateDate = source.UpdateDate; + + // Membership + target.MembershipScenario = GetMembershipScenario(); + target.MemberProviderFieldMapping = GetMemberProviderFieldMapping(); target.Username = source.Username; + target.Email = source.Email; + target.MembershipProperties = _tabsAndPropertiesMapper.MapMembershipProperties(source, context); } // Umbraco.Code.MapAll -Trashed -Edited -Updater -Alias -VariesByCulture diff --git a/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesMapper.cs b/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesMapper.cs index 61c6684093..6877806dee 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesMapper.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Web.Security; using Umbraco.Core; using Umbraco.Core.Mapping; -using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Security; @@ -79,53 +78,15 @@ namespace Umbraco.Web.Models.Mapping } } - var umbracoContext = _umbracoContextAccessor.UmbracoContext; - if (umbracoContext != null - && umbracoContext.Security.CurrentUser != null - && umbracoContext.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings))) - { - var memberTypeLink = string.Format("#/member/memberTypes/edit/{0}", source.ContentTypeId); - - // Replace the doctype property - var docTypeProperty = resolved.SelectMany(x => x.Properties) - .First(x => x.Alias == string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); - docTypeProperty.Value = new List - { - new - { - linkText = source.ContentType.Name, - url = memberTypeLink, - target = "_self", - icon = Constants.Icons.ContentType - } - }; - docTypeProperty.View = "urllist"; - } - return resolved; } - protected override IEnumerable GetCustomGenericProperties(IContentBase content) + public IEnumerable MapMembershipProperties(IMember member, MapperContext context) { - var member = (IMember)content; var membersProvider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); - var genericProperties = new List + var properties = new List { - new ContentPropertyDisplay - { - Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}id", - Label = _localizedTextService.Localize("general","id"), - Value = new List {member.Id.ToString(), member.Key.ToString()}, - View = "idwithguid" - }, - new ContentPropertyDisplay - { - Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}doctype", - Label = _localizedTextService.Localize("content","membertype"), - Value = _localizedTextService.UmbracoDictionaryTranslate(member.ContentType.Name), - View = Current.PropertyEditors[Constants.PropertyEditors.Aliases.Label].GetValueEditor().View - }, GetLoginProperty(_memberService, member, _localizedTextService), new ContentPropertyDisplay { @@ -139,21 +100,20 @@ namespace Umbraco.Web.Models.Mapping { Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}password", Label = _localizedTextService.Localize(null,"password"), - // NOTE: The value here is a json value - but the only property we care about is the generatedPassword one if it exists, the newPassword exists + // NOTE: The value here is a JSON value - but the only property we care about is the generatedPassword one if it exists, the newPassword exists // only when creating a new member and we want to have a generated password pre-filled. Value = new Dictionary { // TODO: why ignoreCase, what are we doing here?! - {"generatedPassword", member.GetAdditionalDataValueIgnoreCase("GeneratedPassword", null)}, - {"newPassword", member.GetAdditionalDataValueIgnoreCase("NewPassword", null)}, + { "generatedPassword", member.GetAdditionalDataValueIgnoreCase("GeneratedPassword", null) }, + { "newPassword", member.GetAdditionalDataValueIgnoreCase("NewPassword", null) }, }, - // TODO: Hard coding this because the changepassword doesn't necessarily need to be a resolvable (real) property editor View = "changepassword", - // initialize the dictionary with the configuration from the default membership provider + // Initialize the dictionary with the configuration from the default membership provider Config = new Dictionary(membersProvider.GetConfiguration(_userService)) { - // the password change toggle will only be displayed if there is already a password assigned. - {"hasPassword", member.RawPasswordValue.IsNullOrWhiteSpace() == false} + // The password change toggle will only be displayed if there is already a password assigned. + { "hasPassword", member.RawPasswordValue.IsNullOrWhiteSpace() == false } } }, new ContentPropertyDisplay @@ -166,7 +126,7 @@ namespace Umbraco.Web.Models.Mapping } }; - return genericProperties; + return properties; } /// diff --git a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesMapper.cs b/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesMapper.cs index d1c6fea923..f3c8af9f41 100644 --- a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesMapper.cs @@ -52,46 +52,23 @@ namespace Umbraco.Web.Models.Mapping var noGroupProperties = content.GetNonGroupedProperties() .Where(x => IgnoreProperties.Contains(x.Alias) == false) // skip ignored .ToList(); - var genericproperties = MapProperties(content, noGroupProperties, context); - - tabs.Add(new Tab - { - Id = 0, - Label = LocalizedTextService.Localize("general", "properties"), - Alias = "Generic properties", - Properties = genericproperties - }); - - var genericProps = tabs.Single(x => x.Id == 0); - - //store the current props to append to the newly inserted ones - var currProps = genericProps.Properties.ToArray(); - - var contentProps = new List(); + var genericProperties = MapProperties(content, noGroupProperties, context); var customProperties = GetCustomGenericProperties(content); if (customProperties != null) { - //add the custom ones - contentProps.AddRange(customProperties); + genericProperties.AddRange(customProperties); } - //now add the user props - contentProps.AddRange(currProps); - - //re-assign - genericProps.Properties = contentProps; - - //Show or hide properties tab based on whether it has or not any properties - if (genericProps.Properties.Any() == false) + if (genericProperties.Count > 0) { - //loop through the tabs, remove the one with the id of zero and exit the loop - for (var i = 0; i < tabs.Count; i++) + tabs.Add(new Tab { - if (tabs[i].Id != 0) continue; - tabs.RemoveAt(i); - break; - } + Id = 0, + Label = LocalizedTextService.Localize("general", "properties"), + Alias = "Generic properties", + Properties = genericProperties + }); } } diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index 5d97bfe4a2..6a267d1587 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -242,7 +242,8 @@ namespace Umbraco.Web.Runtime .Append() .Append() .Append() - .Append(); + .Append() + .Append(); // register back office sections in the order we want them rendered composition.Sections() diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 5f6ab67a42..e930e967f5 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -162,6 +162,7 @@ + @@ -1184,7 +1185,9 @@ - + + ASPXCodeBehind + From 4bf598d9a4f55a28266e7ae0e3d371cda667c8cb Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Mon, 8 Nov 2021 11:22:07 +0100 Subject: [PATCH 05/41] Change IsLockedOut property to readonly when it's true (only allow unlocking members) --- .../propertyeditors/boolean/boolean.html | 1 + .../Mapping/MemberTabsAndPropertiesMapper.cs | 30 +++++++------------ 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/boolean/boolean.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/boolean/boolean.html index aa47e0c667..a0b3f8494d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/boolean/boolean.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/boolean/boolean.html @@ -4,6 +4,7 @@ x.Properties).FirstOrDefault(x => x.Alias == Constants.Conventions.Member.IsLockedOut); - if (isLockedOutProperty?.Value != null && isLockedOutProperty.Value.ToString() != "1") - { - isLockedOutProperty.View = "readonlyvalue"; - isLockedOutProperty.Value = _localizedTextService.Localize("general", "no"); - } - } - else - { - var umbracoProvider = (IUmbracoMemberTypeMembershipProvider)provider; - // This is kind of a hack because a developer is supposed to be allowed to set their property editor - would have been much easier // if we just had all of the membership provider fields on the member table :( // TODO: But is there a way to map the IMember.IsLockedOut to the property ? i dunno. - var isLockedOutProperty = resolved.SelectMany(x => x.Properties).FirstOrDefault(x => x.Alias == umbracoProvider.LockPropertyTypeAlias); - if (isLockedOutProperty?.Value != null && isLockedOutProperty.Value.ToString() != "1") - { - isLockedOutProperty.View = "readonlyvalue"; - isLockedOutProperty.Value = _localizedTextService.Localize("general", "no"); - } + isLockedOutPropertyAlias = umbracoProvider.LockPropertyTypeAlias; + } + + var isLockedOutProperty = resolved.SelectMany(x => x.Properties).FirstOrDefault(x => x.Alias == isLockedOutPropertyAlias); + if (isLockedOutProperty?.Value != null && isLockedOutProperty.Value.ToString() != "1") + { + isLockedOutProperty.Readonly = true; } return resolved; From 4f6a073f796e708cf48583e13c8274976d2f9cfa Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Mon, 8 Nov 2021 11:22:35 +0100 Subject: [PATCH 06/41] Re-bind readonly property state after save --- .../services/contenteditinghelper.service.js | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js index 8e3855cd20..fdf0838394 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js @@ -624,24 +624,29 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt var origProp = allOrigProps[k]; var alias = origProp.alias; var newProp = getNewProp(alias, allNewProps); - if (newProp && !_.isEqual(origProp.value, newProp.value)) { + if (newProp) { + // Always update readonly state + origProp.readonly = newProp.readonly; - //they have changed so set the origContent prop to the new one - var origVal = origProp.value; + // Check whether the value has changed and update accordingly + if (!_.isEqual(origProp.value, newProp.value)) { - origProp.value = newProp.value; + //they have changed so set the origContent prop to the new one + var origVal = origProp.value; - //instead of having a property editor $watch their expression to check if it has - // been updated, instead we'll check for the existence of a special method on their model - // and just call it. - if (Utilities.isFunction(origProp.onValueChanged)) { - //send the newVal + oldVal - origProp.onValueChanged(origProp.value, origVal); + origProp.value = newProp.value; + + //instead of having a property editor $watch their expression to check if it has + // been updated, instead we'll check for the existence of a special method on their model + // and just call it. + if (Utilities.isFunction(origProp.onValueChanged)) { + //send the newVal + oldVal + origProp.onValueChanged(origProp.value, origVal); + } + + changed.push(origProp); } - - changed.push(origProp); } - } } From 1aeed3173b1f199024e61de1a0249f6432c8fcc7 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Mon, 8 Nov 2021 11:47:12 +0100 Subject: [PATCH 07/41] Do not allow deleting tabs with locked properties --- .../common/directives/components/umbgroupsbuilder.directive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js index 44d45263da..79da9e3ac6 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js @@ -582,7 +582,7 @@ }; scope.canRemoveTab = (tab) => { - return tab.inherited !== true; + return scope.canRemoveGroup(tab) && _.every(scope.model.groups.filter(group => group.parentAlias === tab.alias), group => scope.canRemoveGroup(group)); }; scope.setTabOverflowState = (overflowLeft, overflowRight) => { From d9c7640a8df26c4c4966f4921e1b4571e382624f Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 10 Nov 2021 15:31:22 +0100 Subject: [PATCH 08/41] Fixes userpicker filter #11520 --- .../userpicker/userpicker.controller.js | 11 ----------- .../common/infiniteeditors/userpicker/userpicker.html | 3 +-- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.controller.js index 33d526c3cf..e2390a7a1a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.controller.js @@ -10,7 +10,6 @@ vm.usersOptions = {}; vm.selectUser = selectUser; - vm.searchUsers = searchUsers; vm.changePageNumber = changePageNumber; vm.submit = submit; vm.close = close; @@ -87,16 +86,6 @@ users.length = 0; } - var search = _.debounce(function () { - $scope.$apply(function () { - getUsers(); - }); - }, 500); - - function searchUsers() { - search(); - } - function getUsers() { vm.loading = true; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.html index 4e1c6b8e6e..da7a5acf73 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.html @@ -23,13 +23,12 @@ model="vm.usersOptions.filter" label-key="placeholders_filter" text="Type to filter..." - on-change="vm.searchUsers()" css-class="w-100 mb-15" auto-focus="true">
-