diff --git a/src/Umbraco.Core/Constants-Applications.cs b/src/Umbraco.Core/Constants-Applications.cs
index 8b9be02f4f..b84446adc3 100644
--- a/src/Umbraco.Core/Constants-Applications.cs
+++ b/src/Umbraco.Core/Constants-Applications.cs
@@ -118,7 +118,7 @@
public const string Scripts = "scripts";
- public const string Users = "usersV2";
+ public const string Users = "users";
//TODO: Fill in the rest!
}
diff --git a/src/Umbraco.Web.UI/config/trees.config b/src/Umbraco.Web.UI/config/trees.config
index 13da9246b7..12f3110cd1 100644
--- a/src/Umbraco.Web.UI/config/trees.config
+++ b/src/Umbraco.Web.UI/config/trees.config
@@ -24,9 +24,10 @@
-
+
+
@@ -40,5 +41,5 @@
-
+
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml
index e46c065695..416d3b0361 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml
@@ -1370,7 +1370,7 @@ To manage your website, simply open the Umbraco back office and start adding con
Templates
XSLT Files
Analytics
- Users
+ Users
New update ready
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
index 9a5ea92fd6..9cf2fcd92b 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
@@ -1367,6 +1367,7 @@ To manage your website, simply open the Umbraco back office and start adding con
Templates
XSLT Files
Analytics
+ Users
New update ready
diff --git a/src/Umbraco.Web/Editors/DataTypeController.cs b/src/Umbraco.Web/Editors/DataTypeController.cs
index b3e306a48c..522c1f2b48 100644
--- a/src/Umbraco.Web/Editors/DataTypeController.cs
+++ b/src/Umbraco.Web/Editors/DataTypeController.cs
@@ -23,6 +23,7 @@ using System.Text;
namespace Umbraco.Web.Editors
{
+
///
/// The API controller used for editing data types
///
diff --git a/src/Umbraco.Web/Editors/UserController.cs b/src/Umbraco.Web/Editors/UserController.cs
index 363940b924..1cdb243742 100644
--- a/src/Umbraco.Web/Editors/UserController.cs
+++ b/src/Umbraco.Web/Editors/UserController.cs
@@ -1,5 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Net;
+using System.Net.Http;
using System.Web.Http;
+using AutoMapper;
+using ClientDependency.Core;
+using Umbraco.Core;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.Membership;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi.Filters;
using Constants = Umbraco.Core.Constants;
@@ -15,7 +26,7 @@ namespace Umbraco.Web.Editors
///
public UserController()
: this(UmbracoContext.Current)
- {
+ {
}
///
@@ -24,7 +35,87 @@ namespace Umbraco.Web.Editors
///
public UserController(UmbracoContext umbracoContext)
: base(umbracoContext)
- {
+ {
+ }
+
+ ///
+ /// Gets a user by Id
+ ///
+ ///
+ ///
+ public UserDisplay GetById(int id)
+ {
+ var user = Services.UserService.GetUserById(id);
+ if (user == null)
+ {
+ throw new HttpResponseException(HttpStatusCode.NotFound);
+ }
+ return Mapper.Map(user);
+ }
+
+ //TODO: This will probably not be UserDisplay objects since there's probably too much data in the display object for a grid
+ public PagedResult GetPagedUsers(
+ int id,
+ int pageNumber = 1,
+ int pageSize = 0,
+ string orderBy = "SortOrder",
+ Direction orderDirection = Direction.Ascending,
+ string filter = "")
+ {
+
+ //TODO: Make this real, for now this is mock data
+
+ var startId = 100 + ((pageNumber -1) * pageSize);
+ var numUsers = pageSize;
+ var users = new List();
+ var userTypes = Services.UserService.GetAllUserTypes().ToDictionary(x => x.Alias, x => x.Name);
+ var cultures = Services.TextService.GetSupportedCultures().ToDictionary(x => x.Name, x => x.DisplayName);
+ for (int i = 0; i < numUsers; i++)
+ {
+ var display = new UserDisplay
+ {
+ Id = startId,
+ UserType = "writer",
+ AllowedSections = new[] {"content", "media"},
+ AvailableUserTypes = userTypes,
+ Email = "test" + startId + "@test.com",
+ Name = "User " + startId,
+ Culture = "en-US",
+ AvailableCultures = cultures,
+ Path = "-1," + startId,
+ ParentId = -1,
+ StartContentId = -1,
+ StartMediaId = -1
+ };
+ users.Add(display);
+ startId++;
+ }
+
+ return new PagedResult(100, pageNumber, pageSize)
+ {
+ Items = users
+ };
+ }
+
+ public UserDisplay PostSaveUser(UserSave userSave)
+ {
+ if (userSave == null) throw new ArgumentNullException("userSave");
+
+ if (ModelState.IsValid == false)
+ {
+ throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
+ }
+
+ var intId = userSave.Id.TryConvertTo();
+ if (intId.Success == false)
+ throw new HttpResponseException(HttpStatusCode.NotFound);
+
+ var found = Services.UserService.GetUserById(intId.Result);
+ if (found == null)
+ throw new HttpResponseException(HttpStatusCode.NotFound);
+
+ //TODO: More validation, password changing logic, persisting
+ return Mapper.Map(found);
}
///
diff --git a/src/Umbraco.Web/Models/ContentEditing/UserBasic.cs b/src/Umbraco.Web/Models/ContentEditing/UserBasic.cs
index c1cf3ed5c6..07e800d647 100644
--- a/src/Umbraco.Web/Models/ContentEditing/UserBasic.cs
+++ b/src/Umbraco.Web/Models/ContentEditing/UserBasic.cs
@@ -1,14 +1,15 @@
-using System.ComponentModel.DataAnnotations;
+using System;
+using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
using Umbraco.Core.Models.Membership;
namespace Umbraco.Web.Models.ContentEditing
{
///
- /// A basic structure the represents a user
+ /// A bare minimum structure that represents a user, usually attached to other objects
///
[DataContract(Name = "user", Namespace = "")]
- public class UserBasic : System.IComparable
+ public class UserBasic : IComparable
{
[DataMember(Name = "id", IsRequired = true)]
[Required]
@@ -19,9 +20,9 @@ namespace Umbraco.Web.Models.ContentEditing
public string Name { get; set; }
- int System.IComparable.CompareTo(object obj)
+ int IComparable.CompareTo(object obj)
{
- return Name.CompareTo(((UserBasic)obj).Name);
+ return String.Compare(Name, ((UserBasic)obj).Name, StringComparison.Ordinal);
}
}
}
\ 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 d27736576f..0b4bd5132d 100644
--- a/src/Umbraco.Web/Models/ContentEditing/UserDetail.cs
+++ b/src/Umbraco.Web/Models/ContentEditing/UserDetail.cs
@@ -3,7 +3,10 @@ using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
namespace Umbraco.Web.Models.ContentEditing
-{
+{
+ ///
+ /// Represents information for the current user
+ ///
[DataContract(Name = "user", Namespace = "")]
public class UserDetail : UserBasic
{
diff --git a/src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs
new file mode 100644
index 0000000000..d77d9d2019
--- /dev/null
+++ b/src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs
@@ -0,0 +1,59 @@
+using System.Collections.Generic;
+using System.Globalization;
+using System.Runtime.Serialization;
+
+namespace Umbraco.Web.Models.ContentEditing
+{
+ ///
+ /// Represents a user that is being edited
+ ///
+ [DataContract(Name = "user", Namespace = "")]
+ public class UserDisplay : EntityBasic, INotificationModel
+ {
+ public UserDisplay()
+ {
+ Notifications = new List();
+ }
+
+ [DataMember(Name = "culture", IsRequired = true)]
+ public string Culture { get; set; }
+
+ [DataMember(Name = "email", IsRequired = true)]
+ public string Email { get; set; }
+
+ [DataMember(Name = "userType")]
+ public string UserType { get; set; }
+
+ ///
+ /// Gets the available user types (i.e. to populate a drop down)
+ /// The key is the Alias the value is the Name - the Alias is what is used in the UserType property and for persistence
+ ///
+ [DataMember(Name = "availableUserTypes")]
+ public IDictionary AvailableUserTypes { 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
+ ///
+ [DataMember(Name = "availableCultures")]
+ public IDictionary AvailableCultures { get; set; }
+
+ [DataMember(Name = "startContentId")]
+ public int StartContentId { get; set; }
+
+ [DataMember(Name = "startMediaId")]
+ public int StartMediaId { get; set; }
+
+ ///
+ /// A list of sections the user is allowed to view.
+ ///
+ [DataMember(Name = "allowedSections")]
+ public IEnumerable AllowedSections { get; set; }
+
+ ///
+ /// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes.
+ ///
+ [DataMember(Name = "notifications")]
+ public List Notifications { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Models/ContentEditing/UserSave.cs b/src/Umbraco.Web/Models/ContentEditing/UserSave.cs
new file mode 100644
index 0000000000..5fe57e0787
--- /dev/null
+++ b/src/Umbraco.Web/Models/ContentEditing/UserSave.cs
@@ -0,0 +1,51 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Runtime.Serialization;
+
+namespace Umbraco.Web.Models.ContentEditing
+{
+ ///
+ /// Represents the data used to persist a user
+ ///
+ ///
+ /// This will be different from the model used to display a user and we don't want to "Overpost" data back to the server,
+ /// and there will most likely be different bits of data required for updating passwords which will be different from the
+ /// data used to display vs save
+ ///
+ [DataContract(Name = "user", Namespace = "")]
+ public class UserSave : EntityBasic, IValidatableObject
+ {
+ //TODO: There will be more information to save along with the structure for changing passwords
+
+ [DataMember(Name = "locale", IsRequired = true)]
+ [Required]
+ public string Culture { get; set; }
+
+ [DataMember(Name = "email", IsRequired = true)]
+ [Required]
+ [EmailAddress]
+ public string Email { get; set; }
+
+ [DataMember(Name = "userType")]
+ [Required]
+ public string UserType { get; set; }
+
+ [DataMember(Name = "startContentId")]
+ public int StartContentId { get; set; }
+
+ [DataMember(Name = "startMediaId")]
+ public int StartMediaId { get; set; }
+
+ [DataMember(Name = "allowedSections")]
+ public IEnumerable AllowedSections { get; set; }
+
+ public IEnumerable Validate(ValidationContext validationContext)
+ {
+ //TODO: Add other server side validation
+ //if (CultureInfo.GetCultureInfo(Culture))
+ // yield return new ValidationResult("The culture is invalid", new[] { "Culture" });
+
+ yield break;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs b/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs
index 72093b19a8..025de43d40 100644
--- a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs
+++ b/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using AutoMapper;
using Umbraco.Core;
using Umbraco.Core.Models.Mapping;
@@ -15,6 +16,16 @@ namespace Umbraco.Web.Models.Mapping
{
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
{
+ config.CreateMap()
+ .ForMember(detail => detail.Id, opt => opt.MapFrom(user => user.Id))
+ .ForMember(detail => detail.UserType, opt => opt.MapFrom(user => user.UserType.Alias))
+ .ForMember(detail => detail.StartContentId, opt => opt.MapFrom(user => user.StartContentId))
+ .ForMember(detail => detail.StartMediaId, opt => opt.MapFrom(user => user.StartMediaId))
+ .ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.GetUserCulture(applicationContext.Services.TextService)))
+ .ForMember(
+ detail => detail.AvailableUserTypes,
+ opt => opt.MapFrom(user => applicationContext.Services.SectionService.GetSections().ToDictionary(x => x.Alias, x => x.Name)));
+
config.CreateMap()
.ForMember(detail => detail.UserId, opt => opt.MapFrom(user => GetIntId(user.Id)))
.ForMember(detail => detail.UserType, opt => opt.MapFrom(user => user.UserType.Alias))
diff --git a/src/Umbraco.Web/Trees/UsersTreeController.cs b/src/Umbraco.Web/Trees/UserTreeController.cs
similarity index 77%
rename from src/Umbraco.Web/Trees/UsersTreeController.cs
rename to src/Umbraco.Web/Trees/UserTreeController.cs
index 65c289006a..1318934559 100644
--- a/src/Umbraco.Web/Trees/UsersTreeController.cs
+++ b/src/Umbraco.Web/Trees/UserTreeController.cs
@@ -1,44 +1,57 @@
-using System.Net.Http.Formatting;
-using Umbraco.Web.Models.Trees;
-using Umbraco.Web.Mvc;
-using Umbraco.Web.WebApi.Filters;
-using Constants = Umbraco.Core.Constants;
-
-namespace Umbraco.Web.Trees
-{
- [UmbracoTreeAuthorize(Constants.Trees.Users)]
- [Tree(Constants.Applications.Users, Constants.Trees.Users, null, sortOrder: 3)]
- [PluginController("UmbracoTrees")]
- [CoreTree]
- public class UsersTreeController : TreeController
- {
- ///
- /// Helper method to create a root model for a tree
- ///
- ///
- protected override TreeNode CreateRootNode(FormDataCollection queryStrings)
- {
- var root = base.CreateRootNode(queryStrings);
-
- //this will load in a custom UI instead of the dashboard for the root node
- root.RoutePath = string.Format("{0}/{1}/{2}", Constants.Applications.Users, Constants.Trees.Users, "overview");
- root.Icon = "icon-users";
-
- return root;
- }
-
- protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
- {
- var baseUrl = Constants.Applications.Users + "/users/";
-
- var nodes = new TreeNodeCollection();
- return nodes;
- }
-
- protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings)
- {
- var menu = new MenuItemCollection();
- return menu;
- }
- }
+using System.Net.Http.Formatting;
+using Umbraco.Web.Models.Trees;
+using Umbraco.Web.Mvc;
+using Umbraco.Web.WebApi.Filters;
+using Constants = Umbraco.Core.Constants;
+
+namespace Umbraco.Web.Trees
+{
+ [UmbracoTreeAuthorize(Constants.Trees.Users)]
+ [Tree(Constants.Applications.Users, Constants.Trees.Users, null, sortOrder: 3)]
+ [PluginController("UmbracoTrees")]
+ [CoreTree]
+ public class UserTreeController : TreeController
+ {
+ public UserTreeController()
+ {
+ }
+
+ public UserTreeController(UmbracoContext umbracoContext) : base(umbracoContext)
+ {
+ }
+
+ public UserTreeController(UmbracoContext umbracoContext, UmbracoHelper umbracoHelper) : base(umbracoContext, umbracoHelper)
+ {
+ }
+
+ ///
+ /// Helper method to create a root model for a tree
+ ///
+ ///
+ protected override TreeNode CreateRootNode(FormDataCollection queryStrings)
+ {
+ var root = base.CreateRootNode(queryStrings);
+
+ //this will load in a custom UI instead of the dashboard for the root node
+ root.RoutePath = string.Format("{0}/{1}/{2}", Constants.Applications.Users, Constants.Trees.Users, "overview");
+ root.Icon = "icon-users";
+
+ root.HasChildren = false;
+ return root;
+ }
+
+ protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
+ {
+ var baseUrl = Constants.Applications.Users + "/users/";
+
+ var nodes = new TreeNodeCollection();
+ return nodes;
+ }
+
+ protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings)
+ {
+ var menu = new MenuItemCollection();
+ return menu;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index d534dc2ae4..0c651af1fe 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -366,6 +366,8 @@
+
+
@@ -433,7 +435,7 @@
-
+
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadUsers.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadUsers.cs
index 5db7a06dab..c749b6b58b 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadUsers.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadUsers.cs
@@ -34,7 +34,8 @@ namespace umbraco
///
/// Handles loading of all umbraco users into the users application tree
///
- [Tree(Constants.Applications.Users, "users", "Users")]
+ [Tree(Constants.Applications.Users, "users_old", "Users (Legacy)")]
+ //TODO: Remove this tree when ready
public class loadUsers : BaseTree
{
public loadUsers(string application) : base(application) { }