Add administrator editing

This commit is contained in:
2016-09-27 11:56:10 -04:00
parent 75b7c02979
commit 3caf0bd766
13 changed files with 207 additions and 59 deletions
@@ -2,7 +2,6 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web.Mvc; using System.Web.Mvc;
using AutoMapper; using AutoMapper;
using AutoMapper.QueryableExtensions;
using InventoryTraker.Web.Attributes; using InventoryTraker.Web.Attributes;
using InventoryTraker.Web.Core; using InventoryTraker.Web.Core;
using InventoryTraker.Web.Identity; using InventoryTraker.Web.Identity;
@@ -11,9 +10,10 @@ using Microsoft.AspNet.Identity;
namespace InventoryTraker.Web.Controllers namespace InventoryTraker.Web.Controllers
{ {
[Authorize(Roles = ApplicationRoleManager.AdminRoleName)]
public class UserController : ControllerBase public class UserController : ControllerBase
{ {
private readonly ApplicationUserManager _userManager; private readonly ApplicationUserManager _userManager;
private readonly IMapper _mapper; private readonly IMapper _mapper;
public UserController(ApplicationUserManager userManager, IMapper mapper) public UserController(ApplicationUserManager userManager, IMapper mapper)
@@ -29,13 +29,18 @@ namespace InventoryTraker.Web.Controllers
public JsonResult All() public JsonResult All()
{ {
var users = var users =
_userManager from u in _userManager.Users.ToList()
.Users let ad = _userManager.GetRoles(u.Id).Contains(ApplicationRoleManager.AdminRoleName)
.ProjectTo<UserViewModel>(_mapper.ConfigurationProvider) orderby u.UserName
.OrderBy(u => u.UserName); select new UserViewModel
{
UserName = u.UserName,
Email = u.Email,
Administrator = ad
};
return BetterJson(users); return BetterJson(users.ToList());
} }
[ActionLog] [ActionLog]
@@ -57,7 +62,17 @@ namespace InventoryTraker.Web.Controllers
if (!identityResult.Succeeded) if (!identityResult.Succeeded)
return GetErrorListJson(identityResult.Errors.ToArray()); return GetErrorListJson(identityResult.Errors.ToArray());
return BetterJson(_mapper.Map<UserViewModel>(user)); user = _userManager.FindByEmail(form.Email);
if (form.Administrator)
{
var result = _userManager.AddToRole(user.Id, ApplicationRoleManager.AdminRoleName);
if (!result.Succeeded)
return GetErrorListJson(result.Errors.ToArray());
}
var userViewModel = _mapper.Map<UserViewModel>(user);
userViewModel.Administrator = _userManager.IsInRole(user.Id, ApplicationRoleManager.AdminRoleName);
return BetterJson(userViewModel);
} }
[ActionLog] [ActionLog]
@@ -79,12 +94,33 @@ namespace InventoryTraker.Web.Controllers
return GetErrorListJson(resetResult.Errors.ToArray()); return GetErrorListJson(resetResult.Errors.ToArray());
} }
var rolesForUser = _userManager.GetRoles(user.Id);
if (rolesForUser.Contains(ApplicationRoleManager.AdminRoleName) && !form.Administrator)
{
var currentUser = _userManager.FindById(User.Identity.GetUserId());
if (currentUser == user)
return GetErrorListJson("Cannot remove admin from yourself");
var result = _userManager.RemoveFromRole(user.Id, ApplicationRoleManager.AdminRoleName);
if (!result.Succeeded)
return GetErrorListJson(result.Errors.ToArray());
}
else if (!rolesForUser.Contains(ApplicationRoleManager.AdminRoleName) && form.Administrator)
{
var result = _userManager.AddToRole(user.Id, ApplicationRoleManager.AdminRoleName);
if (!result.Succeeded)
return GetErrorListJson(result.Errors.ToArray());
}
var identityResult = _userManager.Update(user); var identityResult = _userManager.Update(user);
if (!identityResult.Succeeded) if (!identityResult.Succeeded)
return GetErrorListJson(identityResult.Errors.ToArray()); return GetErrorListJson(identityResult.Errors.ToArray());
return BetterJson(_mapper.Map<UserViewModel>(user)); var userViewModel = _mapper.Map<UserViewModel>(user);
userViewModel.Administrator = _userManager.IsInRole(user.Id, ApplicationRoleManager.AdminRoleName);
return BetterJson(userViewModel);
} }
} }
} }
+2
View File
@@ -2,6 +2,7 @@
using System.Web.Mvc; using System.Web.Mvc;
using System.Web.Optimization; using System.Web.Optimization;
using System.Web.Routing; using System.Web.Routing;
using InventoryTraker.Web.Migrations;
namespace InventoryTraker.Web namespace InventoryTraker.Web
{ {
@@ -14,6 +15,7 @@ namespace InventoryTraker.Web
BundleConfig.RegisterBundles(BundleTable.Bundles); BundleConfig.RegisterBundles(BundleTable.Bundles);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
EFConfig.Initialize(); EFConfig.Initialize();
SeedData.AddAdminRole();
//SeedData.Init(); //SeedData.Init();
} }
} }
@@ -75,40 +75,68 @@ namespace InventoryTraker.Web.Helpers
var expression = ExpressionForInternal(property); var expression = ExpressionForInternal(property);
//Creates <div class="form-group has-feedback"
// form-group-validation="Name">
var formGroup = new HtmlTag("div")
.AddClasses("form-group", "has-feedback")
.Attr("form-group-validation", name);
var labelText = metadata.DisplayName ?? name.Humanize(LetterCasing.Title); var labelText = metadata.DisplayName ?? name.Humanize(LetterCasing.Title);
//Creates <label class="control-label" for="Name">Name</label> //Creates <label class="control-label" for="Name">Name</label>
var label = new HtmlTag("label") var label = new HtmlTag("label")
.AddClass("control-label")
.Attr("for", name) .Attr("for", name)
.Text(labelText); .Text(labelText);
var tagName = metadata.DataTypeName == "MultilineText" var tagName =
metadata.DataTypeName == "MultilineText"
? "textarea" ? "textarea"
: "input"; : "input";
var placeholder = metadata.Watermark ??
(labelText + "...");
//Creates <input ng-model="expression" //Creates <input ng-model="expression"
// class="form-control" name="Name" type="text" > // class="form-control" name="Name" type="text" >
var input = new HtmlTag(tagName) var input = new HtmlTag(tagName)
.AddClass("form-control")
.Attr("ng-model", expression) .Attr("ng-model", expression)
.Attr("name", name) .Attr("name", name);
.Attr("type", "text")
.Attr("placeholder", placeholder);
ApplyValidationToInput(input, metadata); var formGroup = new HtmlTag("div");
return formGroup if (metadata.ModelType != typeof(bool))
.Append(label) {
.Append(input); label.AddClass("control-label");
var placeholder = metadata.Watermark ??
labelText + "...";
input
.AddClass("form-control")
.Attr("type", "text")
.Attr("placeholder", placeholder);
ApplyValidationToInput(input, metadata);
//Creates <div class="form-group has-feedback"
// form-group-validation="Name">
formGroup
.AddClass("form-group")
.AddClass("has-feedback")
.Attr("form-group-validation", name)
.Append(label)
.Append(input);
}
else if (metadata.ModelType == typeof(bool))
{
label.AddClass("form-check-label");
input
.AddClass("form-check-input")
.Attr("type", "checkbox");
label.Text("")
.Append(input)
.AppendHtml("&nbsp;&nbsp;")
.Append(new HtmlTag("text").NoTag().Text(labelText));
formGroup
.AddClass("form-check")
.Append(label);
}
return formGroup;
} }
private void ApplyValidationToInput(HtmlTag input, ModelMetadata metadata) private void ApplyValidationToInput(HtmlTag input, ModelMetadata metadata)
@@ -2,11 +2,21 @@ using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using InventoryTraker.Web.Core; using InventoryTraker.Web.Core;
using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin; using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security.DataProtection; using Microsoft.Owin.Security.DataProtection;
namespace InventoryTraker.Web.Identity namespace InventoryTraker.Web.Identity
{ {
public class ApplicationRoleManager : RoleManager<IdentityRole>
{
public const string AdminRoleName = "Admin";
public ApplicationRoleManager(IRoleStore<IdentityRole, string> store) : base(store)
{
}
}
public class ApplicationUserManager : UserManager<User> public class ApplicationUserManager : UserManager<User>
{ {
public ApplicationUserManager(IUserStore<User> store, IDataProtectionProvider dataProtectionProvider) public ApplicationUserManager(IUserStore<User> store, IDataProtectionProvider dataProtectionProvider)
@@ -14,6 +14,34 @@ namespace InventoryTraker.Web.Migrations
{ {
public static class SeedData public static class SeedData
{ {
public static void AddAdminRole()
{
using (var context = new AppDbContext())
AddAdminRole(context);
}
private static void AddAdminRole(AppDbContext context)
{
var manager = new ApplicationRoleManager(new RoleStore<IdentityRole>(context));
if (!manager.RoleExists(ApplicationRoleManager.AdminRoleName))
{
var result = manager.Create(new IdentityRole(ApplicationRoleManager.AdminRoleName));
}
// if no users are admins, make them all!
var adminRole = manager.Roles.First(r => r.Name == ApplicationRoleManager.AdminRoleName);
var userManager = new ApplicationUserManager(new UserStore<User>(context), null);
var admins = userManager.Users.Where(u => u.Roles.Any(r => r.RoleId == adminRole.Id));
if (!admins.Any())
{
foreach (var user in userManager.Users.ToList())
{
userManager.AddToRole(user.Id, ApplicationRoleManager.AdminRoleName);
}
}
}
public static void Init() public static void Init()
{ {
using (var context = new AppDbContext()) using (var context = new AppDbContext())
@@ -14,6 +14,9 @@ namespace InventoryTraker.Web.Models
[RegularExpression(@"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}", ErrorMessage = "Must be an email address")] [RegularExpression(@"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}", ErrorMessage = "Must be an email address")]
public string Email { get; set; } public string Email { get; set; }
//[Required]
public bool Administrator { get; set; }
[DataType(DataType.Password)] [DataType(DataType.Password)]
public string Password { get; set; } public string Password { get; set; }
@@ -16,6 +16,8 @@ namespace InventoryTraker.Web.Models
[RegularExpression(@"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}", ErrorMessage = "Must be an email address")] [RegularExpression(@"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}", ErrorMessage = "Must be an email address")]
public string Email { get; set; } public string Email { get; set; }
public bool Administrator { get; set; }
public override string ToString() public override string ToString()
{ {
return $"UserName: {UserName}, email: {Email}"; return $"UserName: {UserName}, email: {Email}";
+30 -22
View File
@@ -2,32 +2,40 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" ng-app="InventoryTraker"> <html lang="en" ng-app="InventoryTraker">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>@ViewBag.Title - InventoryTraker</title> <title>@ViewBag.Title - Inventory Traker</title>
@Styles.Render("~/Content/all-styles") @Styles.Render("~/Content/all-styles")
@RenderSection("Styles", required: false) @RenderSection("Styles", false)
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script> <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script> <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]--> <![endif]-->
</head> </head>
<body> <body>
<div id="wrapper" ng-cloak> <div id="wrapper" ng-cloak>
@(Request.IsAuthenticated ? Html.Partial("_Navigation") : Html.Partial("_NavigationNoAuth")) @(Request.IsAuthenticated ? Html.Partial("_Navigation") : Html.Partial("_NavigationNoAuth"))
<div id="page-wrapper"> <div id="page-wrapper">
<div class="container-fluid"> <div class="container-fluid">
@RenderBody() @RenderBody()
</div>
</div> </div>
<!-- /#page-wrapper -->
</div> </div>
<!-- /#wrapper --> @if (Request.IsAuthenticated)
@Scripts.Render("~/js/all-javascript") {
@RenderSection("Scripts", required: false) <footer class="footer hidden-print">
<div class="container">
<p>Inventory Traker &copy; 2016 Kolpack Software Consulting LLC</p>
</div>
</footer>
}
<!-- /#page-wrapper -->
</div>
<!-- /#wrapper -->
@Scripts.Render("~/js/all-javascript")
@RenderSection("Scripts", false)
</body> </body>
</html> </html>
@@ -1,4 +1,5 @@
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation"> @using InventoryTraker.Web.Identity
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<!-- Brand and toggle get grouped for better mobile display --> <!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header"> <div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
@@ -7,13 +8,16 @@
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<a class="navbar-brand" href="~/">Inventory Traker</a> <a class="navbar-brand" href="~/"><i class="fa fa-fw fa-cubes"></i> Inventory Traker</a>
</div> </div>
<!-- Top Menu Items --> <!-- Top Menu Items -->
<ul class="nav navbar-right top-nav"> <ul class="nav navbar-right top-nav">
<li> @if (User.IsInRole(ApplicationRoleManager.AdminRoleName))
<a href="@(Html.BuildUrlFromExpression<UserController>(c => c.Index()))"><i class="fa fa-fw fa-users"></i> Users</a> {
</li> <li>
<a href="@(Html.BuildUrlFromExpression<UserController>(c => c.Index()))"><i class="fa fa-fw fa-users"></i> Users</a>
</li>
}
<li class="dropdown"> <li class="dropdown">
<a href="" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-user"></i> @User.Identity.Name <b class="caret"></b></a> <a href="" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-user"></i> @User.Identity.Name <b class="caret"></b></a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
@@ -1,6 +1,6 @@
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation"> <nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<!-- Brand and toggle get grouped for better mobile display --> <!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header"> <div class="navbar-header">
<a class="navbar-brand" href="~/">Inventory Traker</a> <a class="navbar-brand" href="~/"><i class="fa fa-fw fa-cubes"></i> Inventory Traker</a>
</div> </div>
</nav> </nav>
+22
View File
@@ -10,9 +10,31 @@ body {
margin-top: 0; margin-top: 0;
} }
.footer {
color: #aaa;
text-align: center;
width: 100%;
/* Set the fixed height of the footer here */
height: 60px;
}
#page-wrapper { #page-wrapper {
padding-top: 50px; padding-top: 50px;
min-height: 100%; min-height: 100%;
padding-bottom: 60px;
margin-bottom: -60px;
}
.navbar-brand i {
color:palegreen
}
.navbar-brand:link,
.navbar-brand:visited,
.navbar-brand:hover,
.navbar-brand:active {
font-weight: bolder;
color: #92B9BD;
} }
.navbar.navbar-fixed-top { .navbar.navbar-fixed-top {
@@ -4,6 +4,7 @@
<th class="control-column"></th> <th class="control-column"></th>
<th>Name</th> <th>Name</th>
<th>Email</th> <th>Email</th>
<th>Administrator</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -11,6 +12,10 @@
<td><a href="" ng-click="vm.edit(user)"><i class="fa fa-edit"></i></a></td> <td><a href="" ng-click="vm.edit(user)"><i class="fa fa-edit"></i></a></td>
<td>{{user.userName}}</td> <td>{{user.userName}}</td>
<td>{{user.email}}</td> <td>{{user.email}}</td>
<td>
<i class="fa fa-check-square-o" ng-show="user.administrator"></i>
<i class="fa fa-square-o" ng-show="!user.administrator"></i>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
+2 -2
View File
@@ -36,8 +36,8 @@
function edit(existingUser, editedUser) { function edit(existingUser, editedUser) {
return $http.post("/User/Edit", editedUser) return $http.post("/User/Edit", editedUser)
.success(function(user) { .success(function(data) {
angular.copy(user, existingUser); angular.copy(data, existingUser);
}); });
} }
} }