diff --git a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js index fb689e37c3..f2376e3acc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js @@ -16,7 +16,7 @@ vm.maxFileSize = Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB"; vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes); vm.usernameIsEmail = Umbraco.Sys.ServerVariables.umbracoSettings.usernameIsEmail; - + //create the initial model for change password vm.changePasswordModel = { config: {}, diff --git a/src/Umbraco.Web.UI.Client/src/views/users/user.html b/src/Umbraco.Web.UI.Client/src/views/users/user.html index 7243586bd3..32c1c3d641 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/user.html +++ b/src/Umbraco.Web.UI.Client/src/views/users/user.html @@ -1,406 +1,27 @@
+ +
- + - + - - - - -
-
+
-
- - - - - - - - - - - Required - - - - - - Required - - - - - - Required - - - - - - - - - - - - - - - - Add - - - - - - - - - - - - - - Add - - - - - - - - - - - - - - Add - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - -
- - - - - - - - - - - - -
- - -
- -
- - -
- -
- - -
- -
- - -
- - - - - - - - - - - - - - -
-


Password reset to value: {{vm.user.resetPasswordValue}}

-
- -
- - -
-
- Status: -
-
- - {{vm.user.userDisplayState.name}} - -
-
- -
-
- Last login: -
-
- {{ vm.user.formattedLastLogin }} - {{ vm.user.name | umbWordLimit:1 }} has not logged in yet -
-
- -
-
- Failed login attempts: -
-
- {{ vm.user.failedPasswordAttempts }} -
-
- -
-
- Last lockout date: -
-
- - {{ vm.user.name | umbWordLimit:1 }} hasn't been locked out - - {{ vm.user.formattedLastLockoutDate }} -
-
- -
-
- Password is last changed: -
-
- - The password hasn't been changed - - {{ vm.user.formattedLastPasswordChangeDate }} -
-
- -
-
- User is created: -
-
- {{ vm.user.formattedCreateDate }} -
-
- -
-
- User is last updated: -
-
- {{ vm.user.formattedUpdateDate }} -
-
- -
- -
- -
+ +
@@ -410,33 +31,30 @@ - + - + - + @@ -447,25 +65,22 @@ - + - + - +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html b/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html new file mode 100644 index 0000000000..4f03b755f5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html @@ -0,0 +1,361 @@ +
+ +
+ + + + + + + + + + + Required + + + + + + Required + + + + + + Required + + + + + + + + + + + + + + + + Add + + + + + + + + + + + + + + Add + + + + + + + + + + + + + + Add + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+ + + + + + + + + + + + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + + + + + + + + + + + + + +
+


Password reset to value: {{model.user.resetPasswordValue}}

+
+ +
+ + +
+
+ Status: +
+
+ + {{model.user.userDisplayState.name}} + +
+
+ +
+
+ Last login: +
+
+ {{ model.user.formattedLastLogin }} + {{ model.user.name | umbWordLimit:1 }} has not logged in yet +
+
+ +
+
+ Failed login attempts: +
+
+ {{ model.user.failedPasswordAttempts }} +
+
+ +
+
+ Last lockout date: +
+
+ + {{ model.user.name | umbWordLimit:1 }} hasn't been locked out + + {{ model.user.formattedLastLockoutDate }} +
+
+ +
+
+ Password is last changed: +
+
+ + The password hasn't been changed + + {{ model.user.formattedLastPasswordChangeDate }} +
+
+ +
+
+ User is created: +
+
+ {{ model.user.formattedCreateDate }} +
+
+ +
+
+ User is last updated: +
+
+ {{ model.user.formattedUpdateDate }} +
+
+ +
+ +
+ +
\ No newline at end of file diff --git a/src/Umbraco.Web/Editors/EditorModelEventArgs.cs b/src/Umbraco.Web/Editors/EditorModelEventArgs.cs new file mode 100644 index 0000000000..153a2d8786 --- /dev/null +++ b/src/Umbraco.Web/Editors/EditorModelEventArgs.cs @@ -0,0 +1,33 @@ +using System; + +namespace Umbraco.Web.Editors +{ + public sealed class EditorModelEventArgs : EditorModelEventArgs + { + public EditorModelEventArgs(EditorModelEventArgs baseArgs) + : base(baseArgs.Model, baseArgs.UmbracoContext) + { + Model = (T)baseArgs.Model; + } + + public EditorModelEventArgs(T model, UmbracoContext umbracoContext) + : base(model, umbracoContext) + { + Model = model; + } + + public new T Model { get; private set; } + } + + public class EditorModelEventArgs : EventArgs + { + public EditorModelEventArgs(object model, UmbracoContext umbracoContext) + { + Model = model; + UmbracoContext = umbracoContext; + } + + public object Model { get; private set; } + public UmbracoContext UmbracoContext { get; private set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/EditorModelEventManager.cs b/src/Umbraco.Web/Editors/EditorModelEventManager.cs index 44454ca6c3..e204bdc044 100644 --- a/src/Umbraco.Web/Editors/EditorModelEventManager.cs +++ b/src/Umbraco.Web/Editors/EditorModelEventManager.cs @@ -1,39 +1,9 @@ -using System; using System.Web.Http.Filters; using Umbraco.Core.Events; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Editors { - public class EditorModelEventArgs : EventArgs - { - public EditorModelEventArgs(object model, UmbracoContext umbracoContext) - { - Model = model; - UmbracoContext = umbracoContext; - } - - public object Model { get; private set; } - public UmbracoContext UmbracoContext { get; private set; } - } - - public sealed class EditorModelEventArgs : EditorModelEventArgs - { - public EditorModelEventArgs(EditorModelEventArgs baseArgs) - : base(baseArgs.Model, baseArgs.UmbracoContext) - { - Model = (T)baseArgs.Model; - } - - public EditorModelEventArgs(T model, UmbracoContext umbracoContext) - : base(model, umbracoContext) - { - Model = model; - } - - public new T Model { get; private set; } - } - /// /// Used to emit events for editor models in the back office /// @@ -42,6 +12,13 @@ namespace Umbraco.Web.Editors public static event TypedEventHandler> SendingContentModel; public static event TypedEventHandler> SendingMediaModel; public static event TypedEventHandler> SendingMemberModel; + public static event TypedEventHandler> SendingUserModel; + + private static void OnSendingUserModel(HttpActionExecutedContext sender, EditorModelEventArgs e) + { + var handler = SendingUserModel; + if (handler != null) handler(sender, e); + } private static void OnSendingContentModel(HttpActionExecutedContext sender, EditorModelEventArgs e) { @@ -85,6 +62,12 @@ namespace Umbraco.Web.Editors { OnSendingMemberModel(sender, new EditorModelEventArgs(e)); } + + var userDisplay = e.Model as UserDisplay; + if (userDisplay != null) + { + OnSendingUserModel(sender, new EditorModelEventArgs(e)); + } } } diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs index 2a8e052f26..e4f79a21a6 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -182,6 +182,7 @@ namespace Umbraco.Web.Editors /// /// /// + [OutgoingEditorModelEvent] public UserDisplay GetById(int id) { var user = Services.UserService.GetUserById(id); diff --git a/src/Umbraco.Web/Models/ContentEditing/EditorNavigation.cs b/src/Umbraco.Web/Models/ContentEditing/EditorNavigation.cs new file mode 100644 index 0000000000..29922750cf --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/EditorNavigation.cs @@ -0,0 +1,26 @@ +using System.Runtime.Serialization; + +namespace Umbraco.Web.Models.ContentEditing +{ + /// + /// A model representing the navigation ("apps") inside an editor in the back office + /// + [DataContract(Name = "user", Namespace = "")] + public class EditorNavigation + { + [DataMember(Name = "name")] + public string Name { get; set; } + + [DataMember(Name = "alias")] + public string Alias { get; set; } + + [DataMember(Name = "icon")] + public string Icon { get; set; } + + [DataMember(Name = "view")] + public string View { get; set; } + + [DataMember(Name = "active")] + public bool Active { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/ContentEditing/UserDetail.cs b/src/Umbraco.Web/Models/ContentEditing/UserDetail.cs index 75e1462ec3..5acad7ea49 100644 --- a/src/Umbraco.Web/Models/ContentEditing/UserDetail.cs +++ b/src/Umbraco.Web/Models/ContentEditing/UserDetail.cs @@ -5,7 +5,7 @@ using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; namespace Umbraco.Web.Models.ContentEditing -{ +{ /// /// Represents information for the current user /// @@ -29,7 +29,7 @@ namespace Umbraco.Web.Models.ContentEditing [Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")] [EditorBrowsable(EditorBrowsableState.Never)] [ReadOnly(true)] - [DataMember(Name = "userType")] + [DataMember(Name = "userType")] public string UserType { get; set; } [ReadOnly(true)] @@ -64,8 +64,8 @@ namespace Umbraco.Web.Models.ContentEditing /// A list of sections the user is allowed to view. /// [DataMember(Name = "allowedSections")] - public IEnumerable AllowedSections { get; set; } - - + public IEnumerable AllowedSections { get; set; } + + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs index 8a79344c8e..1464d9580f 100644 --- a/src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs @@ -18,8 +18,13 @@ namespace Umbraco.Web.Models.ContentEditing AvailableCultures = new Dictionary(); StartContentIds = new List(); StartMediaIds = new List(); + Navigation = new List(); } - + + [DataMember(Name = "navigation")] + [ReadOnly(true)] + public IEnumerable Navigation { get; set; } + /// /// Gets the available cultures (i.e. to populate a drop down) /// The key is the culture stored in the database, the value is the Name diff --git a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs b/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs index 80990fed5b..9262e7eebc 100644 --- a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using AutoMapper; using Umbraco.Core; using Umbraco.Core.Models.Mapping; @@ -231,12 +232,14 @@ namespace Umbraco.Web.Models.Mapping }); //Important! Currently we are never mapping to multiple UserDisplay objects but if we start doing that - // this will cause an N+1 and we'll need to change how this works. + // this will cause an N+1 and we'll need to change how this works. + config.CreateMap() .ForMember(detail => detail.Avatars, opt => opt.MapFrom(user => user.GetUserAvatarUrls(applicationContext.ApplicationCache.RuntimeCache))) .ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.Username)) .ForMember(detail => detail.LastLoginDate, opt => opt.MapFrom(user => user.LastLoginDate == default(DateTime) ? null : (DateTime?) user.LastLoginDate)) .ForMember(detail => detail.UserGroups, opt => opt.MapFrom(user => user.Groups)) + .ForMember(detail => detail.Navigation, opt => opt.MapFrom(user => CreateUserEditorNavigation(applicationContext.Services.TextService))) .ForMember( detail => detail.CalculatedStartContentIds, opt => opt.MapFrom(user => GetStartNodeValues( @@ -255,7 +258,7 @@ namespace Umbraco.Web.Models.Mapping "media/mediaRoot"))) .ForMember( detail => detail.StartContentIds, - opt => opt.MapFrom(user => GetStartNodeValues( + opt => opt.MapFrom(user => GetStartNodeValues( user.StartContentIds.ToArray(), applicationContext.Services.TextService, applicationContext.Services.EntityService, @@ -263,7 +266,7 @@ namespace Umbraco.Web.Models.Mapping "content/contentRoot"))) .ForMember( detail => detail.StartMediaIds, - opt => opt.MapFrom(user => GetStartNodeValues( + opt => opt.MapFrom(user => GetStartNodeValues( user.StartMediaIds.ToArray(), applicationContext.Services.TextService, applicationContext.Services.EntityService, @@ -330,7 +333,7 @@ namespace Umbraco.Web.Models.Mapping //the best we can do here is to return the user's first user group as a IUserType object //but we should attempt to return any group that is the built in ones first var groups = user.Groups.ToArray(); - detail.UserGroups = user.Groups.Select(x => x.Alias).ToArray(); + detail.UserGroups = user.Groups.Select(x => x.Alias).ToArray(); if (groups.Length == 0) { @@ -358,6 +361,21 @@ namespace Umbraco.Web.Models.Mapping .ForMember(detail => detail.UserId, opt => opt.MapFrom(profile => GetIntId(profile.Id))); } + private IEnumerable CreateUserEditorNavigation(ILocalizedTextService textService) + { + return new[] + { + new EditorNavigation + { + Active = true, + Alias = "details", + Icon = "icon-umb-users", + Name = textService.Localize("general/user"), + View = "views/users/views/user/details.html" + } + }; + } + private IEnumerable GetStartNodeValues(int[] startNodeIds, ILocalizedTextService textService, IEntityService entityService, UmbracoObjectTypes objectType, string localizedKey) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 326ed3baa9..5461069713 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -318,6 +318,7 @@ + @@ -374,6 +375,7 @@ +