From 873925106649e97c837b32d7ab264c975f0d6376 Mon Sep 17 00:00:00 2001 From: James Kolpack Date: Sat, 29 Dec 2012 20:41:50 -0500 Subject: [PATCH] Set password operational Sitewide plan for status messages --- .../ViewModels/CreateUserViewModelTests.cs | 2 +- .../ViewModels/EditUserViewModelTests.cs | 50 ++++++++++++ Web.Tests/Web.Tests.csproj | 1 + Web/Content/Site.css | 4 + Web/Controllers/AccountController.cs | 45 +---------- Web/Controllers/CreateLogController.cs | 1 - Web/Controllers/LogController.cs | 8 +- Web/Controllers/UserController.cs | 76 +++++++++++++++---- Web/Controllers/VehicleController.cs | 4 +- Web/DAL/CodeFirstMembershipProvider.cs | 2 +- Web/DAL/CodeFirstRoleProvider.cs | 7 +- Web/DAL/DataService.cs | 17 ++--- Web/Models/User.cs | 5 ++ Web/Utility/CustomExtensions.cs | 4 +- Web/ViewModels/User/CreateUserViewModel.cs | 25 ++---- Web/ViewModels/User/EditUserViewModel.cs | 54 +++++++++++++ Web/ViewModels/User/RolesViewModel.cs | 10 +++ Web/ViewModels/User/SetPasswordViewModel.cs | 26 +++++++ Web/ViewModels/User/UserDetailsViewModel.cs | 30 -------- Web/Views/Account/Manage.cshtml | 2 +- Web/Views/Log/Details.cshtml | 2 + Web/Views/Log/Index.cshtml | 2 + .../DisplayTemplates/_FieldLayout.cshtml | 29 +++++-- Web/Views/Shared/_StatusMessage.cshtml | 13 ++++ Web/Views/User/Details.cshtml | 15 ++-- Web/Views/User/Edit.cshtml | 7 +- Web/Views/User/Index.cshtml | 2 + Web/Views/User/SetPassword.cshtml | 19 +++++ Web/Views/{Shared => User}/_Roles.cshtml | 2 +- Web/Views/Vehicle/Details.cshtml | 2 + Web/Views/Vehicle/Index.cshtml | 2 + Web/Web.csproj | 8 +- 32 files changed, 332 insertions(+), 144 deletions(-) create mode 100644 Web.Tests/ViewModels/EditUserViewModelTests.cs create mode 100644 Web/ViewModels/User/EditUserViewModel.cs create mode 100644 Web/ViewModels/User/RolesViewModel.cs create mode 100644 Web/ViewModels/User/SetPasswordViewModel.cs delete mode 100644 Web/ViewModels/User/UserDetailsViewModel.cs create mode 100644 Web/Views/Shared/_StatusMessage.cshtml create mode 100644 Web/Views/User/SetPassword.cshtml rename Web/Views/{Shared => User}/_Roles.cshtml (74%) diff --git a/Web.Tests/ViewModels/CreateUserViewModelTests.cs b/Web.Tests/ViewModels/CreateUserViewModelTests.cs index 57adb73..7ce9cff 100644 --- a/Web.Tests/ViewModels/CreateUserViewModelTests.cs +++ b/Web.Tests/ViewModels/CreateUserViewModelTests.cs @@ -7,7 +7,7 @@ namespace Web.Tests.ViewModels public class CreateUserViewModelTests { [Test] - public void CloneToUser_CopiesProperties() + public void ToUser_CopiesProperties() { var vm = new CreateUserViewModel(); diff --git a/Web.Tests/ViewModels/EditUserViewModelTests.cs b/Web.Tests/ViewModels/EditUserViewModelTests.cs new file mode 100644 index 0000000..103b5c9 --- /dev/null +++ b/Web.Tests/ViewModels/EditUserViewModelTests.cs @@ -0,0 +1,50 @@ +using System; +using MileageTraker.Web.Models; +using MileageTraker.Web.ViewModels.User; +using NUnit.Framework; + +namespace Web.Tests.ViewModels +{ + [TestFixture] + public class EditUserViewModelTests + { + [Test] + public void EditUser_FromUser() + { + var user = new User(); + + var email = user.Email = "bob@dobalina.com"; + var username = user.Username = "bobdobalina"; + var fullName = user.FullName = "Bob Dobalina"; + var userId = user.UserId = Guid.NewGuid(); + + var vm = new EditUserViewModel(user); + + Assert.That(vm.Email, Is.EqualTo(email)); + Assert.That(vm.Username, Is.EqualTo(username)); + Assert.That(vm.FullName, Is.EqualTo(fullName)); + Assert.That(vm.UserId, Is.EqualTo(userId)); + } + + [Test] + public void UpdateUser() + { + var vm = new EditUserViewModel(); + + var email = vm.Email = "bob@dobalina.com"; + var username = vm.Username = "bobdobalina"; + var fullName = vm.FullName = "Bob Dobalina"; + var roles = vm.Roles = new [] {"Administrator", "Developer"}; + var userId = vm.UserId = Guid.NewGuid(); + vm.UserId = Guid.NewGuid(); + + var user = new User{UserId = userId}; + vm.UpdateUser(user); + + Assert.That(user.Email, Is.EqualTo(email)); + Assert.That(user.Username, Is.EqualTo(username)); + Assert.That(user.FullName, Is.EqualTo(fullName)); + Assert.That(user.UserId, Is.EqualTo(userId)); + } + } +} diff --git a/Web.Tests/Web.Tests.csproj b/Web.Tests/Web.Tests.csproj index 0d94bf3..5049e94 100644 --- a/Web.Tests/Web.Tests.csproj +++ b/Web.Tests/Web.Tests.csproj @@ -90,6 +90,7 @@ + diff --git a/Web/Content/Site.css b/Web/Content/Site.css index 0717bb5..a2c973d 100644 --- a/Web/Content/Site.css +++ b/Web/Content/Site.css @@ -78,6 +78,10 @@ footer { padding: 4px; } +.alert .close { + position: static; +} + .qtip-content dt { line-height: 10px; width: 110px; diff --git a/Web/Controllers/AccountController.cs b/Web/Controllers/AccountController.cs index 7826cb6..87ba7c7 100644 --- a/Web/Controllers/AccountController.cs +++ b/Web/Controllers/AccountController.cs @@ -26,7 +26,7 @@ namespace MileageTraker.Web.Controllers if (success) { FormsAuthentication.SetAuthCookie(model.Username, model.RememberMe); - // TODO: send notification to user + TempData["StatusMessage"] = "Logged in as " + model.Username; return RedirectToLocal(returnUrl); } } @@ -42,7 +42,7 @@ namespace MileageTraker.Web.Controllers { FormsAuthentication.SignOut(); - // TODO: send notification to user + TempData["StatusMessage"] = "Logged off"; return RedirectToAction("Index", "CreateLog"); } @@ -108,7 +108,8 @@ namespace MileageTraker.Web.Controllers if (changePasswordSucceeded) { - return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess }); + TempData["StatusMessage"] = ManageMessageId.ChangePasswordSuccess; + return RedirectToAction("Manage"); } ModelState.AddModelError("", "The current password is incorrect or the new password is invalid."); } @@ -131,43 +132,5 @@ namespace MileageTraker.Web.Controllers ChangePasswordSuccess, SetPasswordSuccess, } - - private static string ErrorCodeToString(MembershipCreateStatus createStatus) - { - // See http://go.microsoft.com/fwlink/?LinkID=177550 for - // a full list of status codes. - switch (createStatus) - { - case MembershipCreateStatus.DuplicateUserName: - return "User name already exists. Please enter a different user name."; - - case MembershipCreateStatus.DuplicateEmail: - return "A user name for that e-mail address already exists. Please enter a different e-mail address."; - - case MembershipCreateStatus.InvalidPassword: - return "The password provided is invalid. Please enter a valid password value."; - - case MembershipCreateStatus.InvalidEmail: - return "The e-mail address provided is invalid. Please check the value and try again."; - - case MembershipCreateStatus.InvalidAnswer: - return "The password retrieval answer provided is invalid. Please check the value and try again."; - - case MembershipCreateStatus.InvalidQuestion: - return "The password retrieval question provided is invalid. Please check the value and try again."; - - case MembershipCreateStatus.InvalidUserName: - return "The user name provided is invalid. Please check the value and try again."; - - case MembershipCreateStatus.ProviderError: - return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator."; - - case MembershipCreateStatus.UserRejected: - return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator."; - - default: - return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator."; - } - } } } diff --git a/Web/Controllers/CreateLogController.cs b/Web/Controllers/CreateLogController.cs index 746ca02..4dbe450 100644 --- a/Web/Controllers/CreateLogController.cs +++ b/Web/Controllers/CreateLogController.cs @@ -2,7 +2,6 @@ using System.Web.Mvc; using MileageTraker.Web.Attributes; using MileageTraker.Web.Models; -using MileageTraker.Web.Utility; using MileageTraker.Web.ViewModels.CreateLog; namespace MileageTraker.Web.Controllers diff --git a/Web/Controllers/LogController.cs b/Web/Controllers/LogController.cs index 6d33bc9..24d3df8 100644 --- a/Web/Controllers/LogController.cs +++ b/Web/Controllers/LogController.cs @@ -98,7 +98,7 @@ namespace MileageTraker.Web.Controllers else { logId = id; - TempData["Message"] = "This is the first log for this vehicle"; + TempData["StatusMessage"] = "This is the first log for this vehicle"; } return RedirectToAction("Details", new {id = logId}); } @@ -114,7 +114,7 @@ namespace MileageTraker.Web.Controllers else { logId = id; - TempData["Message"] = "This is the most recent log for this vehicle"; + TempData["StatusMessage"] = "This is the most recent log for this vehicle"; } return RedirectToAction("Details", new { id = logId }); } @@ -142,6 +142,8 @@ namespace MileageTraker.Web.Controllers log.UserAgent = HttpContext.Request.UserAgent; DataService.AddLog(log); + + TempData["StatusMessage"] = "Log created"; return RedirectToAction("Index"); } @@ -162,6 +164,7 @@ namespace MileageTraker.Web.Controllers if (ModelState.IsValid) { DataService.UpdateLog(log); + TempData["StatusMessage"] = "Log updated"; return RedirectToAction("Details", new{id = log.LogId}); } return View(log); @@ -188,6 +191,7 @@ namespace MileageTraker.Web.Controllers { DataService.DeleteLog(id); + TempData["StatusMessage"] = "Log deleted"; if (Session["LogPage"] != null) return Redirect((string) Session["LogPage"]); return RedirectToAction("Index"); diff --git a/Web/Controllers/UserController.cs b/Web/Controllers/UserController.cs index ff33744..8e4a69a 100644 --- a/Web/Controllers/UserController.cs +++ b/Web/Controllers/UserController.cs @@ -2,8 +2,6 @@ using System.Linq; using System.Web.Mvc; using System.Web.Security; -using MileageTraker.Web.Attributes; -using MileageTraker.Web.Models; using MileageTraker.Web.ViewModels.User; namespace MileageTraker.Web.Controllers @@ -41,8 +39,6 @@ namespace MileageTraker.Web.Controllers { if (ModelState.IsValid) { - var hasRoles = viewModel.Roles != null && viewModel.Roles.Any(); - MembershipCreateStatus membershipCreateStatus; var membershipUser = Membership.CreateUser( @@ -51,7 +47,7 @@ namespace MileageTraker.Web.Controllers viewModel.Email, null, null, - hasRoles, + true, out membershipCreateStatus); if (membershipUser == null) @@ -61,19 +57,19 @@ namespace MileageTraker.Web.Controllers return View(viewModel); } - if (hasRoles) + if (viewModel.Roles != null && viewModel.Roles.Any()) { Roles.AddUserToRoles( membershipUser.UserName, viewModel.Roles); } - var user = DataService.GetUser( - (Guid) membershipUser.ProviderUserKey); + var user = DataService.GetUser((Guid) membershipUser.ProviderUserKey); user.FullName = viewModel.FullName; DataService.UpdateUserPersonalInfo(user); - return RedirectToAction("Index"); + TempData["StatusMessage"] = "User " + user.Username + " created"; + return RedirectToAction("Index"); } viewModel.AvailableRoles = Roles.GetAllRoles(); @@ -87,20 +83,72 @@ namespace MileageTraker.Web.Controllers { return HttpNotFound(); } - return View(user); + + var vm = new EditUserViewModel(user) + { + Roles = Roles.GetRolesForUser(user.Username), + AvailableRoles = Roles.GetAllRoles() + }; + + return View(vm); } [HttpPost] - public ActionResult Edit(User user) + public ActionResult Edit(EditUserViewModel viewModel) { if (ModelState.IsValid) { - DataService.UpdateUser(user); - return RedirectToAction("Index"); + var user = DataService.GetUser(viewModel.UserId); + viewModel.UpdateUser(user); + DataService.UpdateUserPersonalInfo(user); + + Roles.RemoveUserFromRoles(user.Username, Roles.GetAllRoles()); + if (viewModel.Roles != null && viewModel.Roles.Any()) + { + Roles.AddUserToRoles( + user.Username, + viewModel.Roles); + } + + TempData["StatusMessage"] = "Changes saved for " + user.Username; + return RedirectToAction("Index"); } - return View(user); + return View(viewModel); } + public ActionResult SetPassword(Guid id) + { + var user = Membership.GetUser(id); + if (user == null) + { + return HttpNotFound(); + } + var viewModel = new SetPasswordViewModel {UserId = id, Username = user.UserName}; + return View(viewModel); + } + + [HttpPost] + public ActionResult SetPassword(SetPasswordViewModel viewModel) + { + if (ModelState.IsValid) + { + // ChangePassword will throw an exception rather than return false in certain failure scenarios. + try + { + DataService.UpdateUserPassword(viewModel.UserId, viewModel.NewPassword); + TempData["StatusMessage"] = "Password set for " + viewModel.Username; + return RedirectToAction("Details", new { id = viewModel.UserId}); + } + catch (Exception) + { + ModelState.AddModelError("", "The new password is invalid."); + } + } + + // If we got this far, something failed, redisplay form + return View(viewModel); + } + private static string ErrorCodeToString(MembershipCreateStatus createStatus) { // See http://go.microsoft.com/fwlink/?LinkID=177550 for diff --git a/Web/Controllers/VehicleController.cs b/Web/Controllers/VehicleController.cs index 7aa97f8..2a2045c 100644 --- a/Web/Controllers/VehicleController.cs +++ b/Web/Controllers/VehicleController.cs @@ -38,7 +38,8 @@ namespace MileageTraker.Web.Controllers if (ModelState.IsValid) { DataService.AddVehicle(vehicle); - return RedirectToAction("Index"); + TempData["StatusMessage"] = "Vehicle " + vehicle.VehicleId + "created"; + return RedirectToAction("Index"); } return View(vehicle); @@ -56,6 +57,7 @@ namespace MileageTraker.Web.Controllers if (ModelState.IsValid) { DataService.UpdateVehicle(vehicle); + TempData["StatusMessage"] = "Changes saved for vehicle " + vehicle.VehicleId; return RedirectToAction("Details", new { id = vehicle.VehicleId }); } return View(vehicle); diff --git a/Web/DAL/CodeFirstMembershipProvider.cs b/Web/DAL/CodeFirstMembershipProvider.cs index 8f3e9be..1dd6b0d 100644 --- a/Web/DAL/CodeFirstMembershipProvider.cs +++ b/Web/DAL/CodeFirstMembershipProvider.cs @@ -116,7 +116,7 @@ namespace MileageTraker.Web.DAL return newUser.ToMembershipUser(Membership.Provider.Name); } } - + public override bool ValidateUser(string username, string password) { if (string.IsNullOrEmpty(username)) diff --git a/Web/DAL/CodeFirstRoleProvider.cs b/Web/DAL/CodeFirstRoleProvider.cs index 742eec7..c78d0fe 100644 --- a/Web/DAL/CodeFirstRoleProvider.cs +++ b/Web/DAL/CodeFirstRoleProvider.cs @@ -196,13 +196,12 @@ namespace MileageTraker.Web.DAL public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames) { - throw new NotSupportedException(); - using (var context = new MileageTrakerContext()) + using (var context = new DataService()) { foreach (var username in usernames) { var us = username; - var user = context.Users.FirstOrDefault(u => u.Username == us); + var user = context.GetUsers().FirstOrDefault(u => u.Username == us); if (user != null) { foreach (var roleName in roleNames) @@ -214,9 +213,9 @@ namespace MileageTraker.Web.DAL user.Roles.Remove(role); } } + context.UpdateUser(user); } } - context.SaveChanges(); } } } diff --git a/Web/DAL/DataService.cs b/Web/DAL/DataService.cs index 1ed3cdc..98c21d5 100644 --- a/Web/DAL/DataService.cs +++ b/Web/DAL/DataService.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Data; -using System.Data.SqlTypes; using System.Linq; using System.Linq.Expressions; using System.Text.RegularExpressions; @@ -471,6 +470,14 @@ namespace MileageTraker.Web.DAL UpdateUser(original); } + public void UpdateUserPassword(Guid userId, string password) + { + var user = GetUser(userId); + user.Password = Crypto.HashPassword(password); + user.LastPasswordChangedDate = DateTime.Now; + UpdateUser(user); + } + public void UpdateUserLastActivity(string username) { var user = FindUserByUsername(username); @@ -505,13 +512,5 @@ namespace MileageTraker.Web.DAL { return _db.Roles; } - - public Role GetRoleByName(string roleName) - { - return - (from role in _db.Roles - where roleName == role.RoleName - select role).FirstOrDefault(); - } } } \ No newline at end of file diff --git a/Web/Models/User.cs b/Web/Models/User.cs index 268ef13..56616d9 100644 --- a/Web/Models/User.cs +++ b/Web/Models/User.cs @@ -42,6 +42,7 @@ namespace MileageTraker.Web.Models [HiddenInput(DisplayValue = false)] [DataType(DataType.Date)] + [DisplayFormat(DataFormatString = @"{0:MM/dd/yyyy}")] public DateTime LastPasswordFailureDate { get; set; } [HiddenInput(DisplayValue = false)] @@ -50,14 +51,17 @@ namespace MileageTraker.Web.Models [HiddenInput(DisplayValue = false)] [DataType(DataType.Date)] + [DisplayFormat(DataFormatString = @"{0:MM/dd/yyyy}")] public DateTime LastLockoutDate { get; set; } [HiddenInput(DisplayValue = false)] [DataType(DataType.Date)] + [DisplayFormat(DataFormatString = @"{0:MM/dd/yyyy}")] public DateTime LastLoginDate { get; set; } [HiddenInput(DisplayValue = false)] [DataType(DataType.Date)] + [DisplayFormat(DataFormatString = @"{0:MM/dd/yyyy}")] public DateTime Created { get; set; } [HiddenInput] @@ -65,6 +69,7 @@ namespace MileageTraker.Web.Models [HiddenInput] [DataType(DataType.Date)] + [DisplayFormat(DataFormatString = @"{0:MM/dd/yyyy}")] public DateTime LastPasswordChangedDate { get; set; } [HiddenInput(DisplayValue = false)] diff --git a/Web/Utility/CustomExtensions.cs b/Web/Utility/CustomExtensions.cs index 7ae424e..88b79c7 100644 --- a/Web/Utility/CustomExtensions.cs +++ b/Web/Utility/CustomExtensions.cs @@ -107,9 +107,9 @@ namespace MileageTraker.Web.Utility public static string ToVerboseStringHistoric(this TimeSpan ts) { - if (ts.TotalDays == 0) + if (ts.TotalDays < 1) return "Today"; - if (ts.TotalDays == 1) + if (ts.TotalDays < 2) return "Yesterday"; var sb = new StringBuilder(); diff --git a/Web/ViewModels/User/CreateUserViewModel.cs b/Web/ViewModels/User/CreateUserViewModel.cs index b58e002..fbc3052 100644 --- a/Web/ViewModels/User/CreateUserViewModel.cs +++ b/Web/ViewModels/User/CreateUserViewModel.cs @@ -1,13 +1,10 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using AutoMapper; using MileageTraker.Web.Attributes; -using MileageTraker.Web.DAL; -using MileageTraker.Web.Models; namespace MileageTraker.Web.ViewModels.User { - public class CreateUserViewModel + public class CreateUserViewModel : RolesViewModel { [Required] [StringLength(64)] @@ -28,25 +25,13 @@ namespace MileageTraker.Web.ViewModels.User [Required, DataType(DataType.Password)] [StringLength(16, MinimumLength = 6)] + [InputSize("medium")] public string Password { get; set; } - public string[] Roles { get; set; } - - public IList AvailableRoles { get; set; } - public IList SelectedRoles { get; set; } - static CreateUserViewModel() { - Mapper.CreateMap(); - Mapper.CreateMap() - .ConvertUsing( - roleName => - { - using (var ds = new DataService()) - { - return ds.GetRoleByName(roleName); - } - }); + Mapper.CreateMap() + .ForMember(vm => vm.Roles, opt => opt.Ignore()); } public Models.User ToUser() diff --git a/Web/ViewModels/User/EditUserViewModel.cs b/Web/ViewModels/User/EditUserViewModel.cs new file mode 100644 index 0000000..1d327aa --- /dev/null +++ b/Web/ViewModels/User/EditUserViewModel.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Web.Mvc; +using AutoMapper; +using MileageTraker.Web.Attributes; + +namespace MileageTraker.Web.ViewModels.User +{ + public class EditUserViewModel : RolesViewModel + { + [HiddenInput(DisplayValue = false)] + public Guid UserId { get; set; } + + [Required] + [StringLength(64)] + [InputSize("small")] + public string Username { get; set; } + + [Required] + [DataType(DataType.EmailAddress)] + [RegularExpression(@"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}", ErrorMessage = "Must be an email address")] + [InputSize("large")] + public string Email { get; set; } + + [Required] + [StringLength(128)] + [RegularExpression(@"[A-Za-z().]+(\s+[A-Za-z().]+)+", ErrorMessage = "Need complete name")] + [InputSize("medium")] + public string FullName { get; set; } + + static EditUserViewModel() + { + Mapper.CreateMap() + .ForMember(u => u.Roles, opt => opt.Ignore()); + } + + public EditUserViewModel() + { + } + + public EditUserViewModel(Models.User user) + { + Mapper.Map(user, this); + } + + public void UpdateUser(Models.User user) + { + user.Username = this.Username; + user.FullName = this.FullName; + user.Email = this.Email; + } + } +} \ No newline at end of file diff --git a/Web/ViewModels/User/RolesViewModel.cs b/Web/ViewModels/User/RolesViewModel.cs new file mode 100644 index 0000000..0056c40 --- /dev/null +++ b/Web/ViewModels/User/RolesViewModel.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace MileageTraker.Web.ViewModels.User +{ + public class RolesViewModel + { + public string[] Roles { get; set; } + public IList AvailableRoles { get; set; } + } +} \ No newline at end of file diff --git a/Web/ViewModels/User/SetPasswordViewModel.cs b/Web/ViewModels/User/SetPasswordViewModel.cs new file mode 100644 index 0000000..fd1ec5d --- /dev/null +++ b/Web/ViewModels/User/SetPasswordViewModel.cs @@ -0,0 +1,26 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Web.Mvc; + +namespace MileageTraker.Web.ViewModels.User +{ + public class SetPasswordViewModel + { + [HiddenInput(DisplayValue = false)] + public Guid UserId { get; set; } + + [HiddenInput(DisplayValue = false)] + public string Username { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } +} diff --git a/Web/ViewModels/User/UserDetailsViewModel.cs b/Web/ViewModels/User/UserDetailsViewModel.cs deleted file mode 100644 index a277241..0000000 --- a/Web/ViewModels/User/UserDetailsViewModel.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Web; - -namespace MileageTraker.Web.ViewModels.User -{ - public class UserDetailsViewModel - { - public string Username { get; set; } - - public string FirstName { get; set; } - - public string LastName { get; set; } - - public string[] Roles { get; set; } - - [DisplayFormat(DataFormatString = @"{0:MM/dd/yyyy}")] - public DateTime LastActivityDate { get; set; } - - [DisplayFormat(DataFormatString = @"{0:MM/dd/yyyy}")] - public DateTime CreateDate { get; set; } - - public bool IsLockedOut { get; set; } - - [DisplayFormat(DataFormatString = @"{0:MM/dd/yyyy}")] - public DateTime LastLockoutDate { get; set; } - } -} \ No newline at end of file diff --git a/Web/Views/Account/Manage.cshtml b/Web/Views/Account/Manage.cshtml index 3c27708..e97a995 100644 --- a/Web/Views/Account/Manage.cshtml +++ b/Web/Views/Account/Manage.cshtml @@ -5,7 +5,7 @@

@ViewBag.Title

-

@ViewBag.StatusMessage

+@Html.Partial("_StatusMessage")
diff --git a/Web/Views/Log/Details.cshtml b/Web/Views/Log/Details.cshtml index b603dcd..eafb154 100644 --- a/Web/Views/Log/Details.cshtml +++ b/Web/Views/Log/Details.cshtml @@ -6,6 +6,8 @@ @{ Html.RenderPartial("BackToLogs"); } +@Html.Partial("_StatusMessage") +

@ViewBag.Title

diff --git a/Web/Views/Log/Index.cshtml b/Web/Views/Log/Index.cshtml index 79c12dc..0c83779 100644 --- a/Web/Views/Log/Index.cshtml +++ b/Web/Views/Log/Index.cshtml @@ -9,6 +9,8 @@ } +@Html.Partial("_StatusMessage") +

@ViewBag.Title

diff --git a/Web/Views/Shared/DisplayTemplates/_FieldLayout.cshtml b/Web/Views/Shared/DisplayTemplates/_FieldLayout.cshtml index 783f821..979c6ca 100644 --- a/Web/Views/Shared/DisplayTemplates/_FieldLayout.cshtml +++ b/Web/Views/Shared/DisplayTemplates/_FieldLayout.cshtml @@ -10,11 +10,26 @@
@Html.ViewData.ModelMetadata.DisplayName
-
- @RenderBody() - @if (!string.IsNullOrEmpty(units)) - { - @units - } -
+ @if (string.IsNullOrEmpty(color)) + { +
+ @RenderBody() + @if (!string.IsNullOrEmpty(units)) + { + @units + } +
+ } + else + { +
+ + @RenderBody() + + @if (!string.IsNullOrEmpty(units)) + { + @units + } +
+ } \ No newline at end of file diff --git a/Web/Views/Shared/_StatusMessage.cshtml b/Web/Views/Shared/_StatusMessage.cshtml new file mode 100644 index 0000000..1d4bd2b --- /dev/null +++ b/Web/Views/Shared/_StatusMessage.cshtml @@ -0,0 +1,13 @@ +@if (TempData.ContainsKey("StatusMessage")) +{ +

+ + @TempData["StatusMessage"] +

+} else if (ViewBag.StatusMessage != null) +{ +

+ + @ViewBag.StatusMessage +

+} diff --git a/Web/Views/User/Details.cshtml b/Web/Views/User/Details.cshtml index c79572b..6c3acc3 100644 --- a/Web/Views/User/Details.cshtml +++ b/Web/Views/User/Details.cshtml @@ -7,6 +7,8 @@ @{ Html.RenderPartial("BackToUsers"); } +@Html.Partial("_StatusMessage") +

@ViewBag.Title

@@ -31,11 +33,6 @@ - @Html.DisplayFor(m => m.IsLockedOut) - @if (Model.IsLockedOut) { - @Html.DisplayFor(m => m.LastLockoutDate) - } -
@Html.DisplayNameFor(m => m.LastActivityDate) @@ -52,9 +49,15 @@ }
- + + @if (Model.IsLockedOut) { +

+ Locked out on @Html.DisplayTextFor(m => m.LastLockoutDate) +

+ }
@Html.ActionLink("Edit", "Edit", new { id = Model.UserId }, new { @class = "btn" }) + @Html.ActionLink("Set Password", "SetPassword", new { id = Model.UserId }, new { @class = "btn" })
diff --git a/Web/Views/User/Edit.cshtml b/Web/Views/User/Edit.cshtml index 6816876..64c2dd1 100644 --- a/Web/Views/User/Edit.cshtml +++ b/Web/Views/User/Edit.cshtml @@ -1,4 +1,4 @@ -@model MileageTraker.Web.Models.User +@model MileageTraker.Web.ViewModels.User.EditUserViewModel @{ ViewBag.Title = "Edit User"; @@ -16,9 +16,14 @@ @Html.EditorForModel() + @Html.Partial("_Roles", Model)
} + +
+ @Html.ActionLink("Set Password", "SetPassword", new { id = Model.UserId }, new { @class = "btn" }) +
diff --git a/Web/Views/User/Index.cshtml b/Web/Views/User/Index.cshtml index fb2ffdc..f67d01b 100644 --- a/Web/Views/User/Index.cshtml +++ b/Web/Views/User/Index.cshtml @@ -5,6 +5,8 @@ ViewBag.Title = "Users"; var grid = new WebGrid(Model, rowsPerPage: 45); } + +@Html.Partial("_StatusMessage")

@ViewBag.Title

diff --git a/Web/Views/User/SetPassword.cshtml b/Web/Views/User/SetPassword.cshtml new file mode 100644 index 0000000..ec4f9af --- /dev/null +++ b/Web/Views/User/SetPassword.cshtml @@ -0,0 +1,19 @@ +@model MileageTraker.Web.ViewModels.User.SetPasswordViewModel +@{ + ViewBag.Title = "Change Password"; +} + +

@ViewBag.Title

+ +@using (Html.BeginForm("SetPassword", "User", FormMethod.Post, new { @class = "form-horizontal well center-content" })) +{ + @Html.Partial("_ValidationSummary") + @Html.AntiForgeryToken() +
+ Set password for @Model.Username + @Html.EditorForModel() +
+ +
+
+} diff --git a/Web/Views/Shared/_Roles.cshtml b/Web/Views/User/_Roles.cshtml similarity index 74% rename from Web/Views/Shared/_Roles.cshtml rename to Web/Views/User/_Roles.cshtml index 44edcc5..c714225 100644 --- a/Web/Views/Shared/_Roles.cshtml +++ b/Web/Views/User/_Roles.cshtml @@ -1,4 +1,4 @@ -@model MileageTraker.Web.ViewModels.User.CreateUserViewModel +@model MileageTraker.Web.ViewModels.User.RolesViewModel @{ Layout = "~/Views/Shared/EditorTemplates/_FieldLayout.cshtml"; } diff --git a/Web/Views/Vehicle/Details.cshtml b/Web/Views/Vehicle/Details.cshtml index 7387aa8..9769e1b 100644 --- a/Web/Views/Vehicle/Details.cshtml +++ b/Web/Views/Vehicle/Details.cshtml @@ -8,6 +8,8 @@ } +@Html.Partial("_StatusMessage") + @{ Html.RenderPartial("BackToVehicles"); }

@ViewBag.Title

diff --git a/Web/Views/Vehicle/Index.cshtml b/Web/Views/Vehicle/Index.cshtml index c994324..6fe8807 100644 --- a/Web/Views/Vehicle/Index.cshtml +++ b/Web/Views/Vehicle/Index.cshtml @@ -8,6 +8,8 @@ } +@Html.Partial("_StatusMessage") +

@ViewBag.Title

diff --git a/Web/Web.csproj b/Web/Web.csproj index 4bd80d4..fe772f2 100644 --- a/Web/Web.csproj +++ b/Web/Web.csproj @@ -147,8 +147,10 @@ + - + + @@ -273,7 +275,9 @@ - + + +