From b43ca64ac7f62b7f3fd20edefac9b7b482467d61 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 22 Jun 2017 16:32:44 +1000 Subject: [PATCH] Ensures that the auth cookie data contains all of the user's start nodes, fixes persisting the user group icon --- .../Models/Identity/BackOfficeIdentityUser.cs | 5 +- .../Models/Identity/IdentityModelMappings.cs | 8 +- src/Umbraco.Core/Models/UserExtensions.cs | 19 ++ .../Security/BackOfficeUserStore.cs | 42 ++- src/Umbraco.Core/Security/UserData.cs | 10 +- .../services/umbdataformatter.service.js | 303 ++++++++++++++++++ .../src/common/services/util.service.js | 297 ----------------- .../Models/Mapping/UserModelMapper.cs | 1 + 8 files changed, 364 insertions(+), 321 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js diff --git a/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs b/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs index ae046edce9..e3ccbe156e 100644 --- a/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs +++ b/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs @@ -5,6 +5,7 @@ using System.Collections.Specialized; using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNet.Identity; +using Umbraco.Core.Models.Membership; using Umbraco.Core.Security; namespace Umbraco.Core.Models.Identity @@ -16,7 +17,7 @@ namespace Umbraco.Core.Models.Identity { StartMediaIds = new int[] { }; StartContentIds = new int[] { }; - Groups = new string[] { }; + Groups = new IReadOnlyUserGroup[] { }; AllowedSections = new string[] { }; Culture = Configuration.GlobalSettings.DefaultUILanguage; } @@ -44,7 +45,7 @@ namespace Umbraco.Core.Models.Identity /// public int[] StartMediaIds { get; set; } public string[] AllowedSections { get; set; } - public string[] Groups { get; set; } + public IReadOnlyUserGroup[] Groups { get; set; } public string Culture { get; set; } /// diff --git a/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs b/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs index aa8eb7187f..5898cb6dfa 100644 --- a/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs +++ b/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs @@ -25,7 +25,7 @@ namespace Umbraco.Core.Models.Identity .ForMember(user => user.StartMediaIds, expression => expression.MapFrom(user => user.StartMediaIds)) .ForMember(user => user.StartContentIds, expression => expression.MapFrom(user => user.StartContentIds)) .ForMember(user => user.AccessFailedCount, expression => expression.MapFrom(user => user.FailedPasswordAttempts)) - .ForMember(user => user.Groups, expression => expression.MapFrom(user => user.Groups.Select(x => x.Alias).ToArray())) + .ForMember(user => user.Groups, expression => expression.MapFrom(user => user.Groups.ToArray())) .ForMember(user => user.AllowedSections, expression => expression.MapFrom(user => user.AllowedSections.ToArray())); config.CreateMap() @@ -34,8 +34,10 @@ namespace Umbraco.Core.Models.Identity .ForMember(detail => detail.AllowedApplications, opt => opt.MapFrom(user => user.AllowedSections)) .ForMember(detail => detail.Roles, opt => opt.MapFrom(user => user.Groups)) .ForMember(detail => detail.RealName, opt => opt.MapFrom(user => user.Name)) - .ForMember(detail => detail.StartContentNodes, opt => opt.MapFrom(user => user.StartContentIds)) - .ForMember(detail => detail.StartMediaNodes, opt => opt.MapFrom(user => user.StartMediaIds)) + //When mapping to UserData which is used in the authcookie we want ALL start nodes including ones defined on the groups + .ForMember(detail => detail.StartContentNodes, opt => opt.MapFrom(user => user.GetCombinedStartContentIds())) + //When mapping to UserData which is used in the authcookie we want ALL start nodes including ones defined on the groups + .ForMember(detail => detail.StartMediaNodes, opt => opt.MapFrom(user => user.GetCombinedStartMediaIds())) .ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.UserName)) .ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.Culture)) .ForMember(detail => detail.SessionId, opt => opt.MapFrom(user => user.SecurityStamp.IsNullOrWhiteSpace() ? Guid.NewGuid().ToString("N") : user.SecurityStamp)); diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs index 30b80f253c..2dc70df275 100644 --- a/src/Umbraco.Core/Models/UserExtensions.cs +++ b/src/Umbraco.Core/Models/UserExtensions.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Net; using Umbraco.Core.Cache; using Umbraco.Core.IO; +using Umbraco.Core.Models.Identity; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; @@ -21,11 +22,29 @@ namespace Umbraco.Core.Models return user.StartContentIds.Concat(user.Groups.Select(x => x.StartContentId)).Distinct(); } + /// + /// Returns all of the user's assigned start node ids based on ids assigned directly to the BackOfficeIdentityUser object and it's groups + /// + /// + public static IEnumerable GetCombinedStartContentIds(this BackOfficeIdentityUser user) + { + return user.StartContentIds.Concat(user.Groups.Select(x => x.StartContentId)).Distinct(); + } + /// /// Returns all of the user's assigned start node ids based on ids assigned directly to the IUser object and it's groups /// /// public static IEnumerable GetCombinedStartMediaIds(this IUser user) + { + return user.StartMediaIds.Concat(user.Groups.Select(x => x.StartMediaId)).Distinct(); + } + + /// + /// Returns all of the user's assigned start node ids based on ids assigned directly to the BackOfficeIdentityUser object and it's groups + /// + /// + public static IEnumerable GetCombinedStartMediaIds(this BackOfficeIdentityUser user) { return user.StartMediaIds.Concat(user.Groups.Select(x => x.StartMediaId)).Distinct(); } diff --git a/src/Umbraco.Core/Security/BackOfficeUserStore.cs b/src/Umbraco.Core/Security/BackOfficeUserStore.cs index 24477d241a..0873f0a83c 100644 --- a/src/Umbraco.Core/Security/BackOfficeUserStore.cs +++ b/src/Umbraco.Core/Security/BackOfficeUserStore.cs @@ -399,12 +399,16 @@ namespace Umbraco.Core.Security throw new InvalidOperationException("The user id must be an integer to work with the Umbraco"); } - var foundUser = _userService.GetUserById(asInt.Result); - var foundGroup = _userService.GetUserGroupByAlias(roleName); + var foundUser = _userService.GetUserById(asInt.Result); - if (foundUser != null && foundGroup != null) + if (foundUser != null) { - foundUser.AddGroup(foundGroup.ToReadOnlyGroup()); + var foundGroup = _userService.GetUserGroupByAlias(roleName); + + if (foundGroup != null) + { + foundUser.AddGroup(foundGroup.ToReadOnlyGroup()); + } } return Task.FromResult(0); @@ -428,12 +432,16 @@ namespace Umbraco.Core.Security throw new InvalidOperationException("The user id must be an integer to work with the Umbraco"); } - var foundUser = _userService.GetUserById(asInt.Result); - var foundGroup = _userService.GetUserGroupByAlias(roleName); + var foundUser = _userService.GetUserById(asInt.Result); - if (foundUser != null && foundGroup != null) + if (foundUser != null) { - foundUser.RemoveGroup(foundGroup.Alias); + var foundGroup = _userService.GetUserGroupByAlias(roleName); + + if (foundGroup != null) + { + foundUser.RemoveGroup(foundGroup.Alias); + } } return Task.FromResult(0); @@ -448,7 +456,7 @@ namespace Umbraco.Core.Security { ThrowIfDisposed(); if (user == null) throw new ArgumentNullException("user"); - return Task.FromResult((IList)user.Groups.ToList()); + return Task.FromResult((IList)user.Groups.Select(x => x.Alias).ToList()); } /// @@ -460,7 +468,7 @@ namespace Umbraco.Core.Security { ThrowIfDisposed(); if (user == null) throw new ArgumentNullException("user"); - return Task.FromResult(user.Groups.InvariantContains(roleName)); + return Task.FromResult(user.Groups.Select(x => x.Alias).InvariantContains(roleName)); } /// @@ -691,21 +699,21 @@ namespace Umbraco.Core.Security user.SecurityStamp = identityUser.SecurityStamp; } - var userGroups = user.Groups.Select(x => x.Alias).ToArray(); + var userGroupAliases = user.Groups.Select(x => x.Alias).ToArray(); + var identityUserGroupAliases = identityUser.Groups.Select(x => x.Alias).ToArray(); - if (userGroups.ContainsAll(identityUser.Groups) == false - || identityUser.Groups.ContainsAll(userGroups) == false) + if (userGroupAliases.ContainsAll(identityUserGroupAliases) == false + || identityUserGroupAliases.ContainsAll(userGroupAliases) == false) { anythingChanged = true; //clear out the current groups (need to ToArray since we are modifying the iterator) user.ClearGroups(); - //get all of the ones found by alias and add them - var foundGroups = _userService.GetUserGroupsByAlias(identityUser.Groups); - foreach (var group in foundGroups) + //use all of the ones assigned and add them + foreach (var group in identityUser.Groups) { - user.AddGroup(group.ToReadOnlyGroup()); + user.AddGroup(group); } } diff --git a/src/Umbraco.Core/Security/UserData.cs b/src/Umbraco.Core/Security/UserData.cs index 31a7d50f25..8761ada847 100644 --- a/src/Umbraco.Core/Security/UserData.cs +++ b/src/Umbraco.Core/Security/UserData.cs @@ -46,10 +46,16 @@ namespace Umbraco.Core.Security [DataMember(Name = "name")] public string RealName { get; set; } - + + /// + /// The start nodes on the UserData object for the auth ticket contains all of the user's start nodes including ones assigned to their user groups + /// [DataMember(Name = "startContent")] public int[] StartContentNodes { get; set; } - + + /// + /// The start nodes on the UserData object for the auth ticket contains all of the user's start nodes including ones assigned to their user groups + /// [DataMember(Name = "startMedia")] public int[] StartMediaNodes { get; set; } 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 new file mode 100644 index 0000000000..3b55753ee5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js @@ -0,0 +1,303 @@ +(function () { + 'use strict'; + + /** + * @ngdoc service + * @name umbraco.services.umbDataFormatter + * @description A helper object used to format/transform JSON Umbraco data, mostly used for persisting data to the server + **/ + function umbDataFormatter() { + return { + + formatContentTypePostData: function (displayModel, action) { + + //create the save model from the display model + var saveModel = _.pick(displayModel, + 'compositeContentTypes', 'isContainer', 'allowAsRoot', 'allowedTemplates', 'allowedContentTypes', + 'alias', 'description', 'thumbnail', 'name', 'id', 'icon', 'trashed', + 'key', 'parentId', 'alias', 'path'); + + //TODO: Map these + saveModel.allowedTemplates = _.map(displayModel.allowedTemplates, function (t) { return t.alias; }); + saveModel.defaultTemplate = displayModel.defaultTemplate ? displayModel.defaultTemplate.alias : null; + var realGroups = _.reject(displayModel.groups, function (g) { + //do not include these tabs + return g.tabState === "init"; + }); + saveModel.groups = _.map(realGroups, function (g) { + + var saveGroup = _.pick(g, 'inherited', 'id', 'sortOrder', 'name'); + + var realProperties = _.reject(g.properties, function (p) { + //do not include these properties + return p.propertyState === "init" || p.inherited === true; + }); + + var saveProperties = _.map(realProperties, function (p) { + var saveProperty = _.pick(p, 'id', 'alias', 'description', 'validation', 'label', 'sortOrder', 'dataTypeId', 'groupId', 'memberCanEdit', 'showOnMemberProfile'); + return saveProperty; + }); + + saveGroup.properties = saveProperties; + + //if this is an inherited group and there are not non-inherited properties on it, then don't send up the data + if (saveGroup.inherited === true && saveProperties.length === 0) { + return null; + } + + return saveGroup; + }); + + //we don't want any null groups + saveModel.groups = _.reject(saveModel.groups, function (g) { + return !g; + }); + + return saveModel; + }, + + /** formats the display model used to display the data type to the model used to save the data type */ + formatDataTypePostData: function (displayModel, preValues, action) { + var saveModel = { + parentId: displayModel.parentId, + id: displayModel.id, + name: displayModel.name, + selectedEditor: displayModel.selectedEditor, + //set the action on the save model + action: action, + preValues: [] + }; + for (var i = 0; i < preValues.length; i++) { + + saveModel.preValues.push({ + key: preValues[i].alias, + value: preValues[i].value + }); + } + return saveModel; + }, + + /** formats the display model used to display the user to the model used to save the user */ + formatUserPostData: function (displayModel) { + + //create the save model from the display model + var saveModel = _.pick(displayModel, 'id', 'parentId', 'name', 'username', 'culture', 'email', 'startContentIds', 'startMediaIds', 'userGroups', 'message'); + + //make sure the userGroups are just a string array + var currGroups = saveModel.userGroups; + var formattedGroups = []; + for (var i = 0; i < currGroups.length; i++) { + if (!angular.isString(currGroups[i])) { + formattedGroups.push(currGroups[i].alias); + } + else { + formattedGroups.push(currGroups[i]); + } + } + saveModel.userGroups = formattedGroups; + + //make sure the startnodes are just a string array + var props = ["startContentIds", "startMediaIds"]; + for (var m = 0; m < props.length; m++) { + var startIds = saveModel[props[m]]; + if (!startIds) { + continue; + } + var formattedIds = []; + for (var j = 0; j < startIds.length; j++) { + formattedIds.push(startIds[j].id); + } + saveModel[props[m]] = formattedIds; + } + + return saveModel; + }, + + /** formats the display model used to display the user group to the model used to save the user group*/ + formatUserGroupPostData: function (displayModel, action) { + + //create the save model from the display model + var saveModel = _.pick(displayModel, 'id', 'alias', 'name', 'icon', 'sections', 'users', 'startContentId', 'startMediaId'); + //set the action on the save model + saveModel.action = action; + if (!saveModel.id) { + saveModel.id = 0; + } + + //make sure the sections are just a string array + var currSections = saveModel.sections; + var formattedSections = []; + for (var i = 0; i < currSections.length; i++) { + if (!angular.isString(currSections[i])) { + formattedSections.push(currSections[i].alias); + } + else { + formattedSections.push(currSections[i]); + } + } + saveModel.sections = formattedSections; + + //make sure the user are just an int array + var currUsers = saveModel.users; + var formattedUsers = []; + for (var j = 0; j < currUsers.length; j++) { + if (!angular.isNumber(currUsers[j])) { + formattedUsers.push(currUsers[j].id); + } + else { + formattedUsers.push(currUsers[j]); + } + } + saveModel.users = formattedUsers; + + //make sure the startnodes are just an int + var props = ["startContentId", "startMediaId"]; + for (var m = 0; m < props.length; m++) { + var startId = saveModel[props[m]]; + if (!startId) { + continue; + } + saveModel[props[m]] = startId.id; + } + + saveModel.parentId = -1; + if (!saveModel.startContentId) { + saveModel.startContentId = -1; + } + if (!saveModel.startMediaId) { + saveModel.startMediaId = -1; + } + + return saveModel; + }, + + /** 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 + + 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; + saveModel.username = propLogin.value; + saveModel.password = propPass.value; + + var selectedGroups = []; + for (var n in propGroups.value) { + if (propGroups.value[n] === true) { + selectedGroups.push(n); + } + } + saveModel.memberGroups = selectedGroups; + + //turn the dictionary into an array of pairs + 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]; + }); + if (foundAlias) { + //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": + saveModel.isLockedOut = prop.value.toString() === "1" ? true : false; + break; + case "umbracoMemberApproved": + saveModel.isApproved = prop.value.toString() === "1" ? true : false; + break; + case "umbracoMemberComments": + saveModel.comments = prop.value; + break; + } + } + }); + }); + + + + return saveModel; + }, + + /** formats the display model used to display the media to the model used to save the media */ + formatMediaPostData: function (displayModel, action) { + //NOTE: the display model inherits from the save model so we can in theory just post up the display model but + // we don't want to post all of the data as it is unecessary. + var saveModel = { + id: displayModel.id, + properties: [], + name: displayModel.name, + contentTypeAlias: displayModel.contentTypeAlias, + parentId: displayModel.parentId, + //set the action on the save model + action: action + }; + + _.each(displayModel.tabs, function (tab) { + + _.each(tab.properties, function (prop) { + + //don't include the custom generic tab properties + if (!prop.alias.startsWith("_umb_")) { + saveModel.properties.push({ + id: prop.id, + alias: prop.alias, + value: prop.value + }); + } + + }); + }); + + return saveModel; + }, + + /** formats the display model used to display the content to the model used to save the content */ + formatContentPostData: function (displayModel, action) { + + //this is basically the same as for media but we need to explicitly add some extra properties + var saveModel = this.formatMediaPostData(displayModel, action); + + var genericTab = _.find(displayModel.tabs, function (item) { + return item.id === 0; + }); + + var propExpireDate = _.find(genericTab.properties, function (item) { + return item.alias === "_umb_expiredate"; + }); + var propReleaseDate = _.find(genericTab.properties, function (item) { + return item.alias === "_umb_releasedate"; + }); + var propTemplate = _.find(genericTab.properties, function (item) { + return item.alias === "_umb_template"; + }); + saveModel.expireDate = propExpireDate.value; + saveModel.releaseDate = propReleaseDate.value; + saveModel.templateAlias = propTemplate.value; + + return saveModel; + } + }; + } + angular.module('umbraco.services').factory('umbDataFormatter', umbDataFormatter); + +})(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js index ebab7497ca..54863be0e4 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js @@ -575,303 +575,6 @@ function umbPropEditorHelper() { angular.module('umbraco.services').factory('umbPropEditorHelper', umbPropEditorHelper); -/** -* @ngdoc service -* @name umbraco.services.umbDataFormatter -* @description A helper object used to format/transform JSON Umbraco data, mostly used for persisting data to the server -**/ -function umbDataFormatter() { - return { - - formatContentTypePostData: function (displayModel, action) { - - //create the save model from the display model - var saveModel = _.pick(displayModel, - 'compositeContentTypes', 'isContainer', 'allowAsRoot', 'allowedTemplates', 'allowedContentTypes', - 'alias', 'description', 'thumbnail', 'name', 'id', 'icon', 'trashed', - 'key', 'parentId', 'alias', 'path'); - - //TODO: Map these - saveModel.allowedTemplates = _.map(displayModel.allowedTemplates, function (t) { return t.alias; }); - saveModel.defaultTemplate = displayModel.defaultTemplate ? displayModel.defaultTemplate.alias : null; - var realGroups = _.reject(displayModel.groups, function (g) { - //do not include these tabs - return g.tabState === "init"; - }); - saveModel.groups = _.map(realGroups, function (g) { - - var saveGroup = _.pick(g, 'inherited', 'id', 'sortOrder', 'name'); - - var realProperties = _.reject(g.properties, function (p) { - //do not include these properties - return p.propertyState === "init" || p.inherited === true; - }); - - var saveProperties = _.map(realProperties, function (p) { - var saveProperty = _.pick(p, 'id', 'alias', 'description', 'validation', 'label', 'sortOrder', 'dataTypeId', 'groupId', 'memberCanEdit', 'showOnMemberProfile'); - return saveProperty; - }); - - saveGroup.properties = saveProperties; - - //if this is an inherited group and there are not non-inherited properties on it, then don't send up the data - if (saveGroup.inherited === true && saveProperties.length === 0) { - return null; - } - - return saveGroup; - }); - - //we don't want any null groups - saveModel.groups = _.reject(saveModel.groups, function (g) { - return !g; - }); - - return saveModel; - }, - - /** formats the display model used to display the data type to the model used to save the data type */ - formatDataTypePostData: function (displayModel, preValues, action) { - var saveModel = { - parentId: displayModel.parentId, - id: displayModel.id, - name: displayModel.name, - selectedEditor: displayModel.selectedEditor, - //set the action on the save model - action: action, - preValues: [] - }; - for (var i = 0; i < preValues.length; i++) { - - saveModel.preValues.push({ - key: preValues[i].alias, - value: preValues[i].value - }); - } - return saveModel; - }, - - /** formats the display model used to display the user to the model used to save the user */ - formatUserPostData: function (displayModel) { - - //create the save model from the display model - var saveModel = _.pick(displayModel, 'id', 'parentId', 'name', 'username', 'culture', 'email', 'startContentIds', 'startMediaIds', 'userGroups', 'message'); - - //make sure the userGroups are just a string array - var currGroups = saveModel.userGroups; - var formattedGroups = []; - for (var i = 0; i < currGroups.length; i++) { - if (!angular.isString(currGroups[i])) { - formattedGroups.push(currGroups[i].alias); - } - else { - formattedGroups.push(currGroups[i]); - } - } - saveModel.userGroups = formattedGroups; - - //make sure the startnodes are just a string array - var props = ["startContentIds", "startMediaIds"]; - for (var m = 0; m < props.length; m++) { - var startIds = saveModel[props[m]]; - if (!startIds) { - continue; - } - var formattedIds = []; - for (var j = 0; j < startIds.length; j++) { - formattedIds.push(startIds[j].id); - } - saveModel[props[m]] = formattedIds; - } - - return saveModel; - }, - - /** formats the display model used to display the user group to the model used to save the user group*/ - formatUserGroupPostData: function (displayModel, action) { - - //create the save model from the display model - var saveModel = _.pick(displayModel, 'id', 'alias', 'name', 'sections', 'users', 'startContentId', 'startMediaId'); - //set the action on the save model - saveModel.action = action; - if (!saveModel.id) { - saveModel.id = 0; - } - - //make sure the sections are just a string array - var currSections = saveModel.sections; - var formattedSections = []; - for (var i = 0; i < currSections.length; i++) { - if (!angular.isString(currSections[i])) { - formattedSections.push(currSections[i].alias); - } - else { - formattedSections.push(currSections[i]); - } - } - saveModel.sections = formattedSections; - - //make sure the user are just an int array - var currUsers = saveModel.users; - var formattedUsers = []; - for (var j = 0; j < currUsers.length; j++) { - if (!angular.isNumber(currUsers[j])) { - formattedUsers.push(currUsers[j].id); - } - else { - formattedUsers.push(currUsers[j]); - } - } - saveModel.users = formattedUsers; - - //make sure the startnodes are just an int - var props = ["startContentId", "startMediaId"]; - for (var m = 0; m < props.length; m++) { - var startId = saveModel[props[m]]; - if (!startId) { - continue; - } - saveModel[props[m]] = startId.id; - } - - saveModel.parentId = -1; - if (!saveModel.startContentId) { - saveModel.startContentId = -1; - } - if (!saveModel.startMediaId) { - saveModel.startMediaId = -1; - } - - return saveModel; - }, - - /** 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 - - 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; - saveModel.username = propLogin.value; - saveModel.password = propPass.value; - - var selectedGroups = []; - for (var n in propGroups.value) { - if (propGroups.value[n] === true) { - selectedGroups.push(n); - } - } - saveModel.memberGroups = selectedGroups; - - //turn the dictionary into an array of pairs - 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]; - }); - if (foundAlias) { - //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": - saveModel.isLockedOut = prop.value.toString() === "1" ? true : false; - break; - case "umbracoMemberApproved": - saveModel.isApproved = prop.value.toString() === "1" ? true : false; - break; - case "umbracoMemberComments": - saveModel.comments = prop.value; - break; - } - } - }); - }); - return saveModel; - }, - - /** formats the display model used to display the media to the model used to save the media */ - formatMediaPostData: function (displayModel, action) { - //NOTE: the display model inherits from the save model so we can in theory just post up the display model but - // we don't want to post all of the data as it is unecessary. - var saveModel = { - id: displayModel.id, - properties: [], - name: displayModel.name, - contentTypeAlias: displayModel.contentTypeAlias, - parentId: displayModel.parentId, - //set the action on the save model - action: action - }; - - _.each(displayModel.tabs, function (tab) { - - _.each(tab.properties, function (prop) { - - //don't include the custom generic tab properties - if (!prop.alias.startsWith("_umb_")) { - saveModel.properties.push({ - id: prop.id, - alias: prop.alias, - value: prop.value - }); - } - - }); - }); - - return saveModel; - }, - - /** formats the display model used to display the content to the model used to save the content */ - formatContentPostData: function (displayModel, action) { - - //this is basically the same as for media but we need to explicitly add some extra properties - var saveModel = this.formatMediaPostData(displayModel, action); - - var genericTab = _.find(displayModel.tabs, function (item) { - return item.id === 0; - }); - - var propExpireDate = _.find(genericTab.properties, function (item) { - return item.alias === "_umb_expiredate"; - }); - var propReleaseDate = _.find(genericTab.properties, function (item) { - return item.alias === "_umb_releasedate"; - }); - var propTemplate = _.find(genericTab.properties, function (item) { - return item.alias === "_umb_template"; - }); - saveModel.expireDate = propExpireDate.value; - saveModel.releaseDate = propReleaseDate.value; - saveModel.templateAlias = propTemplate.value; - - return saveModel; - } - }; -} -angular.module('umbraco.services').factory('umbDataFormatter', umbDataFormatter); - - diff --git a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs b/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs index 36774bdf85..dfb7f60764 100644 --- a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs @@ -28,6 +28,7 @@ namespace Umbraco.Web.Models.Mapping .IgnoreAllUnmapped() .ForMember(user => user.Alias, expression => expression.MapFrom(save => save.Alias)) .ForMember(user => user.Name, expression => expression.MapFrom(save => save.Name)) + .ForMember(user => user.Icon, expression => expression.MapFrom(save => save.Icon)) .ForMember(user => user.StartMediaId, expression => expression.MapFrom(save => save.StartMediaId)) .ForMember(user => user.StartContentId, expression => expression.MapFrom(save => save.StartContentId)) .AfterMap((save, userGroup) =>