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 +