From a7c314d7286ceaf37eacf92d1172ca10ef6d3780 Mon Sep 17 00:00:00 2001 From: Uncled1023 Date: Tue, 16 Jan 2018 22:36:11 -0800 Subject: [PATCH] - Implemented invite codes to use for registration. - Reorganized settings pages into individual pages. --- .../Admin/Controllers/AdminController.cs | 19 + Teknik/Areas/Admin/Scripts/UserInfo.js | 38 +- .../Areas/Admin/Views/Admin/UserInfo.cshtml | 7 + .../Areas/User/Controllers/UserController.cs | 332 ++++++++++++++++-- Teknik/Areas/User/Models/InviteCode.cs | 27 ++ Teknik/Areas/User/Models/User.cs | 6 + Teknik/Areas/User/Scripts/BlogSettings.js | 29 ++ Teknik/Areas/User/Scripts/InviteSettings.js | 35 ++ Teknik/Areas/User/Scripts/ProfileSettings.js | 31 ++ Teknik/Areas/User/Scripts/ResetPass.js | 49 +++ .../Scripts/{User.js => SecuritySettings.js} | 133 +------ Teknik/Areas/User/Scripts/Settings.js | 22 ++ Teknik/Areas/User/Scripts/UploadSettings.js | 29 ++ Teknik/Areas/User/UserAreaRegistration.cs | 82 ++++- Teknik/Areas/User/Utility/UserHelper.cs | 33 ++ .../User/ViewModels/BlogSettingsViewModel.cs | 22 ++ .../User/ViewModels/InviteCodeViewModel.cs | 21 ++ .../ViewModels/InviteSettingsViewModel.cs | 21 ++ .../ViewModels/ProfileSettingsViewModel.cs | 27 ++ .../User/ViewModels/RegisterViewModel.cs | 7 +- .../ViewModels/SecuritySettingsViewModel.cs | 50 +++ .../User/ViewModels/SettingsViewModel.cs | 16 +- .../ViewModels/UploadSettingsViewModel.cs | 19 + Teknik/Areas/User/Views/User/Register.cshtml | 25 +- .../User/Views/User/ResetPassword.cshtml | 6 +- .../User/ResetPasswordVerification.cshtml | 4 +- Teknik/Areas/User/Views/User/Settings.cshtml | 255 -------------- .../User/{ => Settings}/AuthToken.cshtml | 0 .../Views/User/Settings/BlogSettings.cshtml | 36 ++ .../Views/User/Settings/InviteCode.cshtml | 21 ++ .../Views/User/Settings/InviteSettings.cshtml | 59 ++++ .../User/Settings/ProfileSettings.cshtml | 41 +++ .../User/Settings/SecuritySettings.cshtml | 178 ++++++++++ .../User/Views/User/Settings/Settings.cshtml | 51 +++ .../Views/User/Settings/SettingsBase.cshtml | 12 + .../Views/User/Settings/UploadSettings.cshtml | 36 ++ .../User/Views/User/_LoginPartial.cshtml | 14 +- Teknik/Models/TeknikEntities.cs | 18 +- Teknik/Scripts/common.js | 39 +- Teknik/Teknik.csproj | 26 +- Utilities/Configuration/UserConfig.cs | 4 +- 41 files changed, 1422 insertions(+), 458 deletions(-) create mode 100644 Teknik/Areas/User/Models/InviteCode.cs create mode 100644 Teknik/Areas/User/Scripts/BlogSettings.js create mode 100644 Teknik/Areas/User/Scripts/InviteSettings.js create mode 100644 Teknik/Areas/User/Scripts/ProfileSettings.js create mode 100644 Teknik/Areas/User/Scripts/ResetPass.js rename Teknik/Areas/User/Scripts/{User.js => SecuritySettings.js} (69%) create mode 100644 Teknik/Areas/User/Scripts/Settings.js create mode 100644 Teknik/Areas/User/Scripts/UploadSettings.js create mode 100644 Teknik/Areas/User/ViewModels/BlogSettingsViewModel.cs create mode 100644 Teknik/Areas/User/ViewModels/InviteCodeViewModel.cs create mode 100644 Teknik/Areas/User/ViewModels/InviteSettingsViewModel.cs create mode 100644 Teknik/Areas/User/ViewModels/ProfileSettingsViewModel.cs create mode 100644 Teknik/Areas/User/ViewModels/SecuritySettingsViewModel.cs create mode 100644 Teknik/Areas/User/ViewModels/UploadSettingsViewModel.cs delete mode 100644 Teknik/Areas/User/Views/User/Settings.cshtml rename Teknik/Areas/User/Views/User/{ => Settings}/AuthToken.cshtml (100%) create mode 100644 Teknik/Areas/User/Views/User/Settings/BlogSettings.cshtml create mode 100644 Teknik/Areas/User/Views/User/Settings/InviteCode.cshtml create mode 100644 Teknik/Areas/User/Views/User/Settings/InviteSettings.cshtml create mode 100644 Teknik/Areas/User/Views/User/Settings/ProfileSettings.cshtml create mode 100644 Teknik/Areas/User/Views/User/Settings/SecuritySettings.cshtml create mode 100644 Teknik/Areas/User/Views/User/Settings/Settings.cshtml create mode 100644 Teknik/Areas/User/Views/User/Settings/SettingsBase.cshtml create mode 100644 Teknik/Areas/User/Views/User/Settings/UploadSettings.cshtml diff --git a/Teknik/Areas/Admin/Controllers/AdminController.cs b/Teknik/Areas/Admin/Controllers/AdminController.cs index 008c148..7b7a19d 100644 --- a/Teknik/Areas/Admin/Controllers/AdminController.cs +++ b/Teknik/Areas/Admin/Controllers/AdminController.cs @@ -132,5 +132,24 @@ namespace Teknik.Areas.Admin.Controllers } return Redirect(Url.SubRouteUrl("error", "Error.Http404")); } + + [HttpPost] + [ValidateAntiForgeryToken] + public ActionResult CreateInviteCode(string username) + { + if (UserHelper.UserExists(db, username)) + { + User user = UserHelper.GetUser(db, username); + InviteCode inviteCode = db.InviteCodes.Create(); + inviteCode.Active = true; + inviteCode.Code = Guid.NewGuid().ToString(); + inviteCode.Owner = user; + db.InviteCodes.Add(inviteCode); + db.SaveChanges(); + + return Json(new { result = new { code = inviteCode.Code } }); + } + return Redirect(Url.SubRouteUrl("error", "Error.Http404")); + } } } diff --git a/Teknik/Areas/Admin/Scripts/UserInfo.js b/Teknik/Areas/Admin/Scripts/UserInfo.js index cff2c16..2c76f90 100644 --- a/Teknik/Areas/Admin/Scripts/UserInfo.js +++ b/Teknik/Areas/Admin/Scripts/UserInfo.js @@ -9,15 +9,15 @@ $(function () { data: AddAntiForgeryToken({ username: username, accountType: selected }), success: function (html) { if (html) { - if (html.error) { - $("#top_msg").css('display', 'inline', 'important'); - $("#top_msg").html('
' + html.error.message + '
'); - } - else { + if (html.result.success) { $("#top_msg").css('display', 'none'); $("#top_msg").html(''); alert('Successfully changed the account type for \'' + username + '\' to type: ' + selected); } + else { + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
' + parseErrorMessage(html) + '
'); + } } } }); @@ -32,18 +32,36 @@ $(function () { data: AddAntiForgeryToken({ username: username, accountStatus: selected }), success: function (html) { if (html) { - if (html.error) { - $("#top_msg").css('display', 'inline', 'important'); - $("#top_msg").html('
' + html.error.message + '
'); - } - else { + if (html.result.success) { $("#top_msg").css('display', 'none'); $("#top_msg").html(''); alert('Successfully changed the account status for \'' + username + '\' to: ' + selected); } + else { + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
' + parseErrorMessage(html) + '
'); + } } } }); }); + $('#createInviteCode').click(function () { + $.ajax({ + type: "POST", + url: createInviteCode, + data: AddAntiForgeryToken({ username: username }), + success: function (html) { + if (html.result) { + $("#top_msg").css('display', 'none'); + $("#top_msg").html(''); + alert('Successfully created invite code for \'' + username + '\': ' + html.result.code); + } + else { + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
' + parseErrorMessage(html) + '
'); + } + } + }); + }); }); diff --git a/Teknik/Areas/Admin/Views/Admin/UserInfo.cshtml b/Teknik/Areas/Admin/Views/Admin/UserInfo.cshtml index e25fedb..19592b3 100644 --- a/Teknik/Areas/Admin/Views/Admin/UserInfo.cshtml +++ b/Teknik/Areas/Admin/Views/Admin/UserInfo.cshtml @@ -6,6 +6,7 @@ // We need to define the action URLs for the script var editAccountType = '@Url.SubRouteUrl("admin", "Admin.Action", new { action = "EditUserAccountType" })'; var editAccountStatus = '@Url.SubRouteUrl("admin", "Admin.Action", new { action = "EditUserAccountStatus" })'; + var createInviteCode = '@Url.SubRouteUrl("admin", "Admin.Action", new { action = "CreateInviteCode" })'; var username = '@Model.Username'; @@ -52,4 +53,10 @@ +
+
+
+ +
+
diff --git a/Teknik/Areas/User/Controllers/UserController.cs b/Teknik/Areas/User/Controllers/UserController.cs index 6358a7d..9efed33 100644 --- a/Teknik/Areas/User/Controllers/UserController.cs +++ b/Teknik/Areas/User/Controllers/UserController.cs @@ -102,23 +102,56 @@ namespace Teknik.Areas.Users.Controllers [TrackPageView] public ActionResult Settings() { - string username = User.Identity.Name; - - SettingsViewModel model = new SettingsViewModel(); - ViewBag.Title = "User Does Not Exist - " + Config.Title; - ViewBag.Description = "The User does not exist"; + return Redirect(Url.SubRouteUrl("user", "User.SecuritySettings")); + } + [TrackPageView] + public ActionResult ProfileSettings() + { using (TeknikEntities db = new TeknikEntities()) { + string username = User.Identity.Name; User user = UserHelper.GetUser(db, username); if (user != null) { Session["AuthenticatedUser"] = user; - ViewBag.Title = "Settings - " + Config.Title; - ViewBag.Description = "Your " + Config.Title + " Settings"; + ViewBag.Title = "Profile Settings - " + Config.Title; + ViewBag.Description = "Your " + Config.Title + " Profile Settings"; + ProfileSettingsViewModel model = new ProfileSettingsViewModel(); + model.Page = "Profile"; + model.UserID = user.UserId; + model.Username = user.Username; + model.About = user.UserSettings.About; + model.Quote = user.UserSettings.Quote; + model.Website = user.UserSettings.Website; + + return View("/Areas/User/Views/User/Settings/ProfileSettings.cshtml", model); + } + } + + return Redirect(Url.SubRouteUrl("error", "Error.Http403")); + } + + [TrackPageView] + public ActionResult SecuritySettings() + { + using (TeknikEntities db = new TeknikEntities()) + { + string username = User.Identity.Name; + User user = UserHelper.GetUser(db, username); + + if (user != null) + { + Session["AuthenticatedUser"] = user; + + ViewBag.Title = "Security Settings - " + Config.Title; + ViewBag.Description = "Your " + Config.Title + " Security Settings"; + + SecuritySettingsViewModel model = new SecuritySettingsViewModel(); + model.Page = "Security"; model.UserID = user.UserId; model.Username = user.Username; model.TrustedDeviceCount = user.TrustedDevices.Count; @@ -133,16 +166,126 @@ namespace Teknik.Areas.Users.Controllers model.AuthTokens.Add(tokenModel); } - model.UserSettings = user.UserSettings; - model.SecuritySettings = user.SecuritySettings; - model.BlogSettings = user.BlogSettings; - model.UploadSettings = user.UploadSettings; + model.PgpPublicKey = user.SecuritySettings.PGPSignature; + model.RecoveryEmail = user.SecuritySettings.RecoveryEmail; + model.RecoveryVerified = user.SecuritySettings.RecoveryVerified; + model.AllowTrustedDevices = user.SecuritySettings.AllowTrustedDevices; + model.TwoFactorEnabled = user.SecuritySettings.TwoFactorEnabled; + model.TwoFactorKey = user.SecuritySettings.TwoFactorKey; - return View(model); + return View("/Areas/User/Views/User/Settings/SecuritySettings.cshtml", model); } } - model.Error = true; - return View(model); + + return Redirect(Url.SubRouteUrl("error", "Error.Http403")); + } + + [TrackPageView] + public ActionResult InviteSettings() + { + using (TeknikEntities db = new TeknikEntities()) + { + string username = User.Identity.Name; + User user = UserHelper.GetUser(db, username); + + if (user != null) + { + Session["AuthenticatedUser"] = user; + + ViewBag.Title = "Invite Settings - " + Config.Title; + ViewBag.Description = "Your " + Config.Title + " Invite Settings"; + + InviteSettingsViewModel model = new InviteSettingsViewModel(); + model.Page = "Invite"; + model.UserID = user.UserId; + model.Username = user.Username; + + List availableCodes = new List(); + List claimedCodes = new List(); + if (user.OwnedInviteCodes != null) + { + foreach (InviteCode inviteCode in user.OwnedInviteCodes.Where(c => c.Active)) + { + InviteCodeViewModel inviteCodeViewModel = new InviteCodeViewModel(); + inviteCodeViewModel.ClaimedUser = inviteCode.ClaimedUser; + inviteCodeViewModel.Active = inviteCode.Active; + inviteCodeViewModel.Code = inviteCode.Code; + inviteCodeViewModel.InviteCodeId = inviteCode.InviteCodeId; + inviteCodeViewModel.Owner = inviteCode.Owner; + + if (inviteCode.ClaimedUser == null) + availableCodes.Add(inviteCodeViewModel); + + if (inviteCode.ClaimedUser != null) + claimedCodes.Add(inviteCodeViewModel); + } + } + + model.AvailableCodes = availableCodes; + model.ClaimedCodes = claimedCodes; + + return View("/Areas/User/Views/User/Settings/InviteSettings.cshtml", model); + } + } + + return Redirect(Url.SubRouteUrl("error", "Error.Http403")); + } + + [TrackPageView] + public ActionResult BlogSettings() + { + using (TeknikEntities db = new TeknikEntities()) + { + string username = User.Identity.Name; + User user = UserHelper.GetUser(db, username); + + if (user != null) + { + Session["AuthenticatedUser"] = user; + + ViewBag.Title = "Blog Settings - " + Config.Title; + ViewBag.Description = "Your " + Config.Title + " Blog Settings"; + + BlogSettingsViewModel model = new BlogSettingsViewModel(); + model.Page = "Blog"; + model.UserID = user.UserId; + model.Username = user.Username; + model.Title = user.BlogSettings.Title; + model.Description = user.BlogSettings.Description; + + return View("/Areas/User/Views/User/Settings/BlogSettings.cshtml", model); + } + } + + return Redirect(Url.SubRouteUrl("error", "Error.Http403")); + } + + [TrackPageView] + public ActionResult UploadSettings() + { + using (TeknikEntities db = new TeknikEntities()) + { + string username = User.Identity.Name; + User user = UserHelper.GetUser(db, username); + + if (user != null) + { + Session["AuthenticatedUser"] = user; + + ViewBag.Title = "Upload Settings - " + Config.Title; + ViewBag.Description = "Your " + Config.Title + " Upload Settings"; + + UploadSettingsViewModel model = new UploadSettingsViewModel(); + model.Page = "Upload"; + model.UserID = user.UserId; + model.Username = user.Username; + model.Encrypt = user.UploadSettings.Encrypt; + + return View("/Areas/User/Views/User/Settings/UploadSettings.cshtml", model); + } + } + + return Redirect(Url.SubRouteUrl("error", "Error.Http403")); } [HttpGet] @@ -295,9 +438,10 @@ namespace Teknik.Areas.Users.Controllers [HttpGet] [TrackPageView] [AllowAnonymous] - public ActionResult Register(string ReturnUrl) + public ActionResult Register(string inviteCode, string ReturnUrl) { RegisterViewModel model = new RegisterViewModel(); + model.InviteCode = inviteCode; model.ReturnUrl = ReturnUrl; return View("/Areas/User/Views/User/ViewRegistration.cshtml", model); @@ -331,6 +475,18 @@ namespace Teknik.Areas.Users.Controllers model.ErrorMessage = "Passwords must match"; } + // Validate the Invite Code + if (!model.Error && Config.UserConfig.InviteCodeRequired && string.IsNullOrEmpty(model.InviteCode)) + { + model.Error = true; + model.ErrorMessage = "An Invite Code is required to register"; + } + if (!model.Error && !string.IsNullOrEmpty(model.InviteCode) && db.InviteCodes.Where(c => c.Code == model.InviteCode && c.Active && c.ClaimedUser == null).FirstOrDefault() == null) + { + model.Error = true; + model.ErrorMessage = "Invalid Invite Code"; + } + // PGP Key valid? if (!model.Error && !string.IsNullOrEmpty(model.PublicKey) && !PGP.IsPublicKey(model.PublicKey)) { @@ -355,6 +511,16 @@ namespace Teknik.Areas.Users.Controllers if (!string.IsNullOrEmpty(model.RecoveryEmail)) newUser.SecuritySettings.RecoveryEmail = model.RecoveryEmail; + // if they provided an invite code, let's assign them to it + if (!string.IsNullOrEmpty(model.InviteCode)) + { + InviteCode code = db.InviteCodes.Where(c => c.Code == model.InviteCode).FirstOrDefault(); + db.Entry(code).State = EntityState.Modified; + db.SaveChanges(); + + newUser.ClaimedInviteCode = code; + } + UserHelper.AddAccount(db, Config, newUser, model.Password); // If they have a recovery email, let's send a verification @@ -389,7 +555,70 @@ namespace Teknik.Areas.Users.Controllers [HttpPost] [ValidateAntiForgeryToken] - public ActionResult Edit(EditSettingsViewModel settings) + public ActionResult EditBlog(BlogSettingsViewModel settings) + { + if (ModelState.IsValid) + { + try + { + using (TeknikEntities db = new TeknikEntities()) + { + User user = UserHelper.GetUser(db, User.Identity.Name); + if (user != null) + { + // Blogs + user.BlogSettings.Title = settings.Title; + user.BlogSettings.Description = settings.Description; + + UserHelper.EditAccount(db, Config, user); + return Json(new { result = true }); + } + return Json(new { error = "User does not exist" }); + } + } + catch (Exception ex) + { + return Json(new { error = ex.GetFullMessage(true) }); + } + } + return Json(new { error = "Invalid Parameters" }); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public ActionResult EditProfile(ProfileSettingsViewModel settings) + { + if (ModelState.IsValid) + { + try + { + using (TeknikEntities db = new TeknikEntities()) + { + User user = UserHelper.GetUser(db, User.Identity.Name); + if (user != null) + { + // Profile Info + user.UserSettings.Website = settings.Website; + user.UserSettings.Quote = settings.Quote; + user.UserSettings.About = settings.About; + + UserHelper.EditAccount(db, Config, user); + return Json(new { result = true }); + } + return Json(new { error = "User does not exist" }); + } + } + catch (Exception ex) + { + return Json(new { error = ex.GetFullMessage(true) }); + } + } + return Json(new { error = "Invalid Parameters" }); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public ActionResult EditSecurity(SecuritySettingsViewModel settings) { if (ModelState.IsValid) { @@ -401,7 +630,6 @@ namespace Teknik.Areas.Users.Controllers if (user != null) { bool changePass = false; - string email = string.Format("{0}@{1}", User.Identity.Name, Config.EmailConfig.Domain); // Changing Password? if (!string.IsNullOrEmpty(settings.CurrentPassword) && (!string.IsNullOrEmpty(settings.NewPassword) || !string.IsNullOrEmpty(settings.NewPasswordConfirm))) { @@ -488,18 +716,6 @@ namespace Teknik.Areas.Users.Controllers } user.SecuritySettings.TwoFactorKey = newKey; - // Profile Info - user.UserSettings.Website = settings.Website; - user.UserSettings.Quote = settings.Quote; - user.UserSettings.About = settings.About; - - // Blogs - user.BlogSettings.Title = settings.BlogTitle; - user.BlogSettings.Description = settings.BlogDesc; - - // Uploads - user.UploadSettings.Encrypt = settings.Encrypt; - UserHelper.EditAccount(db, Config, user, changePass, settings.NewPassword); // If they have a recovery email, let's send a verification @@ -528,6 +744,36 @@ namespace Teknik.Areas.Users.Controllers return Json(new { error = "Invalid Parameters" }); } + [HttpPost] + [ValidateAntiForgeryToken] + public ActionResult EditUpload(UploadSettingsViewModel settings) + { + if (ModelState.IsValid) + { + try + { + using (TeknikEntities db = new TeknikEntities()) + { + User user = UserHelper.GetUser(db, User.Identity.Name); + if (user != null) + { + // Profile Info + user.UploadSettings.Encrypt = settings.Encrypt; + + UserHelper.EditAccount(db, Config, user); + return Json(new { result = true }); + } + return Json(new { error = "User does not exist" }); + } + } + catch (Exception ex) + { + return Json(new { error = ex.GetFullMessage(true) }); + } + } + return Json(new { error = "Invalid Parameters" }); + } + [HttpPost] [ValidateAntiForgeryToken] public ActionResult Delete() @@ -913,7 +1159,7 @@ namespace Teknik.Areas.Users.Controllers model.Name = token.Name; model.LastDateUsed = token.LastDateUsed; - return Json(new { result = new { token = newTokenStr, html = PartialView("~/Areas/User/Views/User/AuthToken.cshtml", model).RenderToString() } }); + return Json(new { result = new { token = newTokenStr, html = PartialView("~/Areas/User/Views/User/Settings/AuthToken.cshtml", model).RenderToString() } }); } return Json(new { error = "Unable to generate Auth Token" }); } @@ -1022,5 +1268,31 @@ namespace Teknik.Areas.Users.Controllers return Json(new { error = ex.GetFullMessage(true) }); } } + + [HttpPost] + [ValidateAntiForgeryToken] + public ActionResult CreateInviteCodeLink(int inviteCodeId) + { + try + { + using (TeknikEntities db = new TeknikEntities()) + { + InviteCode code = db.InviteCodes.Where(c => c.InviteCodeId == inviteCodeId).FirstOrDefault(); + if (code != null) + { + if (code.Owner.UserId == User.Info.UserId) + { + return Json(new { result = Url.SubRouteUrl("user", "User.Register", new { inviteCode = code.Code })}); + } + return Json(new { error = "Invite Code not associated with this user"}); + } + return Json(new { error = "Invalid Invite Code" }); + } + } + catch (Exception ex) + { + return Json(new { error = ex.GetFullMessage(true) }); + } + } } } diff --git a/Teknik/Areas/User/Models/InviteCode.cs b/Teknik/Areas/User/Models/InviteCode.cs new file mode 100644 index 0000000..9f993de --- /dev/null +++ b/Teknik/Areas/User/Models/InviteCode.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using Teknik.Attributes; + +namespace Teknik.Areas.Users.Models +{ + public class InviteCode + { + public int InviteCodeId { get; set; } + + public bool Active { get; set; } + + [CaseSensitive] + public string Code { get; set; } + + public virtual User Owner { get; set; } + + public virtual User ClaimedUser { get; set; } + + public InviteCode() + { + Active = false; + } + } +} diff --git a/Teknik/Areas/User/Models/User.cs b/Teknik/Areas/User/Models/User.cs index 2b02f75..f1b4b1b 100644 --- a/Teknik/Areas/User/Models/User.cs +++ b/Teknik/Areas/User/Models/User.cs @@ -24,6 +24,10 @@ namespace Teknik.Areas.Users.Models public DateTime LastSeen { get; set; } + public virtual InviteCode ClaimedInviteCode { get; set; } + + public virtual ICollection OwnedInviteCodes { get; set; } + public AccountType AccountType { get; set; } public AccountStatus AccountStatus { get; set; } @@ -62,6 +66,8 @@ namespace Teknik.Areas.Users.Models Groups = new List(); TrustedDevices = new List(); AuthTokens = new List(); + ClaimedInviteCode = null; + OwnedInviteCodes = new List(); } } } diff --git a/Teknik/Areas/User/Scripts/BlogSettings.js b/Teknik/Areas/User/Scripts/BlogSettings.js new file mode 100644 index 0000000..93be3d3 --- /dev/null +++ b/Teknik/Areas/User/Scripts/BlogSettings.js @@ -0,0 +1,29 @@ +$(document).ready(function () { + $("#update_submit").click(function () { + // Start Updating Animation + $.blockUI({ message: '

Updating...

' }); + + blog_title = $("#update_blog_title").val(); + blog_desc = $("#update_blog_description").val(); + $.ajax({ + type: "POST", + url: editURL, + data: AddAntiForgeryToken({ + Title: blog_title, + Description: blog_desc, + }), + success: function (html) { + $.unblockUI(); + if (html.result) { + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
Settings Saved!
'); + } + else { + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
' + parseErrorMessage(html) + '
'); + } + } + }); + return false; + }); +}); diff --git a/Teknik/Areas/User/Scripts/InviteSettings.js b/Teknik/Areas/User/Scripts/InviteSettings.js new file mode 100644 index 0000000..c010576 --- /dev/null +++ b/Teknik/Areas/User/Scripts/InviteSettings.js @@ -0,0 +1,35 @@ +$(document).ready(function () { + $('[data-toggle="popover"]').popover(); + + $('[data-toggle="popover"]').on('shown.bs.popover', function () { + var $this = $(this); + setTimeout(function() { + $this.popover('hide'); + }, 3000); + }); +}); + +function copyCode(inviteCodeId, inviteCode) { + copyTextToClipboard(inviteCode); + $('#copyCode_' + inviteCodeId + '').popover('show'); +} + +function createExternalLink(inviteCodeId) { + $.ajax({ + type: "POST", + url: createExternalLinkURL, + data: AddAntiForgeryToken({ inviteCodeId: inviteCodeId }), + success: function (response) { + if (response.result) { + bootbox.dialog({ + title: "Send this link to someone to claim this invite code.", + message: '' + }); + } + else { + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
' + parseErrorMessage(response) + '
'); + } + } + }); +} diff --git a/Teknik/Areas/User/Scripts/ProfileSettings.js b/Teknik/Areas/User/Scripts/ProfileSettings.js new file mode 100644 index 0000000..dbfe9a9 --- /dev/null +++ b/Teknik/Areas/User/Scripts/ProfileSettings.js @@ -0,0 +1,31 @@ +$(document).ready(function () { + $("#update_submit").click(function () { + // Start Updating Animation + $.blockUI({ message: '

Updating...

' }); + + website = $("#update_website").val(); + quote = $("#update_quote").val(); + about = $("#update_about").val(); + $.ajax({ + type: "POST", + url: editURL, + data: AddAntiForgeryToken({ + Website: website, + Quote: quote, + About: about + }), + success: function (html) { + $.unblockUI(); + if (html.result) { + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
Settings Saved!
'); + } + else { + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
' + parseErrorMessage(html) + '
'); + } + } + }); + return false; + }); +}); diff --git a/Teknik/Areas/User/Scripts/ResetPass.js b/Teknik/Areas/User/Scripts/ResetPass.js new file mode 100644 index 0000000..36c6209 --- /dev/null +++ b/Teknik/Areas/User/Scripts/ResetPass.js @@ -0,0 +1,49 @@ +$(document).ready(function () { + $("#reset_pass_send_submit").click(function () { + var form = $('#reset_pass_send'); + username = $("#reset_username").val(); + $.ajax({ + type: "POST", + url: form.attr('action'), + data: AddAntiForgeryToken({ + username: username + }), + success: function (html) { + if (html.result) { + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
The Password Reset Link has been sent to your recovery email.
'); + } + else { + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
' + parseErrorMessage(html) + '
'); + } + } + }); + return false; + }); + + $("#setNewPass_submit").click(function () { + var form = $('#setNewPass'); + password = $("#setNewPass_Password").val(); + confirmPassword = $("#setNewPass_ConfirmPassword").val(); + $.ajax({ + type: "POST", + url: form.attr('action'), + data: AddAntiForgeryToken({ + Password: password, + PasswordConfirm: confirmPassword + }), + success: function (html) { + if (html.result) { + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
Password has successfully been reset.
'); + } + else { + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
' + parseErrorMessage(html) + '
'); + } + } + }); + return false; + }); +}); diff --git a/Teknik/Areas/User/Scripts/User.js b/Teknik/Areas/User/Scripts/SecuritySettings.js similarity index 69% rename from Teknik/Areas/User/Scripts/User.js rename to Teknik/Areas/User/Scripts/SecuritySettings.js index edeccd4..f9b149d 100644 --- a/Teknik/Areas/User/Scripts/User.js +++ b/Teknik/Areas/User/Scripts/SecuritySettings.js @@ -1,5 +1,4 @@ -$(document).ready(function () { - $("[name='update_upload_encrypt']").bootstrapSwitch(); +$(document).ready(function () { $("[name='update_security_two_factor']").bootstrapSwitch(); $("[name='update_security_allow_trusted']").bootstrapSwitch(); @@ -10,18 +9,12 @@ data: AddAntiForgeryToken({}), success: function (html) { if (html.result) { - window.location.reload(); + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
Recovery Email Verification Sent.
'); } else { - errorMsg = html; - if (html.error) { - errorMsg = html.error; - if (html.error.message) { - errorMsg = html.error.message; - } - } $("#top_msg").css('display', 'inline', 'important'); - $("#top_msg").html('
' + errorMsg + '
'); + $("#top_msg").html('
' + parseErrorMessage(html) + '
'); } } }); @@ -51,15 +44,8 @@ $("#authSetupStatus").html('
Success!
'); } else { - errorMsg = html; - if (html.error) { - errorMsg = html.error; - if (html.error.message) { - errorMsg = html.error.message; - } - } $("#authSetupStatus").css('display', 'inline', 'important'); - $("#authSetupStatus").html('
' + errorMsg + '
'); + $("#authSetupStatus").html('
' + parseErrorMessage(html) + '
'); } } }); @@ -77,15 +63,8 @@ $("#top_msg").html('
Successfully Cleared Trusted Devices
'); } else { - errorMsg = html; - if (html.error) { - errorMsg = html.error; - if (html.error.message) { - errorMsg = html.error.message; - } - } $("#top_msg").css('display', 'inline', 'important'); - $("#top_msg").html('
' + errorMsg + '
'); + $("#top_msg").html('
' + parseErrorMessage(html) + '
'); } } }); @@ -119,15 +98,8 @@ }); } else { - errorMsg = response; - if (response.error) { - errorMsg = response.error; - if (response.error.message) { - errorMsg = response.error.message; - } - } $("#top_msg").css('display', 'inline', 'important'); - $("#top_msg").html('
' + errorMsg + '
'); + $("#top_msg").html('
' + parseErrorMessage(response) + '
'); } } }); @@ -147,43 +119,8 @@ $('#authTokenList').html('
  • No Authentication Tokens
  • '); } else { - errorMsg = response; - if (response.error) { - errorMsg = response.error; - if (response.error.message) { - errorMsg = response.error.message; - } - } $("#top_msg").css('display', 'inline', 'important'); - $("#top_msg").html('
    ' + errorMsg + '
    '); - } - } - }); - } - }); - }); - - $('#delete_account').click(function () { - bootbox.confirm("Are you sure you want to delete your account?", function (result) { - if (result) { - $.ajax({ - type: "POST", - url: deleteUserURL, - data: AddAntiForgeryToken({}), - success: function (html) { - if (html.result) { - window.location.replace(homeUrl); - } - else { - errorMsg = html; - if (html.error) { - errorMsg = html.error; - if (html.error.message) { - errorMsg = html.error.message; - } - } - $("#top_msg").css('display', 'inline', 'important'); - $("#top_msg").html('
    ' + errorMsg + '
    '); + $("#top_msg").html('
    ' + parseErrorMessage(response) + '
    '); } } }); @@ -202,15 +139,9 @@ update_security_allow_trusted = $("#update_security_allow_trusted").is(":checked"); update_security_two_factor = $("#update_security_two_factor").is(":checked"); recovery = $("#update_recovery_email").val(); - website = $("#update_website").val(); - quote = $("#update_quote").val(); - about = $("#update_about").val(); - blog_title = $("#update_blog_title").val(); - blog_desc = $("#update_blog_description").val(); - upload_encrypt = $("#update_upload_encrypt").is(":checked"); $.ajax({ type: "POST", - url: editUserURL, + url: editURL, data: AddAntiForgeryToken({ CurrentPassword: current_password, NewPassword: password, @@ -218,13 +149,7 @@ PgpPublicKey: update_pgp_public_key, AllowTrustedDevices: update_security_allow_trusted, TwoFactorEnabled: update_security_two_factor, - RecoveryEmail: recovery, - Website: website, - Quote: quote, - About: about, - BlogTitle: blog_title, - BlogDesc: blog_desc, - Encrypt: upload_encrypt + RecoveryEmail: recovery }), success: function (html) { $.unblockUI(); @@ -238,15 +163,13 @@ } else { - window.location.reload(); + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
    Settings Saved!
    '); } } else { - var error = html; - if (html.error) - error = html.error; $("#top_msg").css('display', 'inline', 'important'); - $("#top_msg").html('
    ' + error + '
    '); + $("#top_msg").html('
    ' + parseErrorMessage(html) + '
    '); } } }); @@ -268,11 +191,8 @@ $("#top_msg").html('
    The Password Reset Link has been sent to your recovery email.
    '); } else { - var error = html; - if (html.error) - error = html.error; $("#top_msg").css('display', 'inline', 'important'); - $("#top_msg").html('
    ' + html.error + '
    '); + $("#top_msg").html('
    ' + parseErrorMessage(html) + '
    '); } } }); @@ -296,11 +216,8 @@ $("#top_msg").html('
    Password has successfully been reset.
    '); } else { - var error = html; - if (html.error) - error = html.error; $("#top_msg").css('display', 'inline', 'important'); - $("#top_msg").html('
    ' + html.error + '
    '); + $("#top_msg").html('
    ' + parseErrorMessage(html) + '
    '); } } }); @@ -320,15 +237,8 @@ function editAuthToken(authTokenId) { $('#authTokenName_' + authTokenId).html(response.result.name); } else { - errorMsg = response; - if (response.error) { - errorMsg = response.error; - if (response.error.message) { - errorMsg = response.error.message; - } - } $("#top_msg").css('display', 'inline', 'important'); - $("#top_msg").html('
    ' + errorMsg + '
    '); + $("#top_msg").html('
    ' + parseErrorMessage(response) + '
    '); } } }); @@ -351,18 +261,11 @@ function deleteAuthToken(authTokenId) { } } else { - errorMsg = response; - if (response.error) { - errorMsg = response.error; - if (response.error.message) { - errorMsg = response.error.message; - } - } $("#top_msg").css('display', 'inline', 'important'); - $("#top_msg").html('
    ' + errorMsg + '
    '); + $("#top_msg").html('
    ' + parseErrorMessage(response) + '
    '); } } }); } }); -} \ No newline at end of file +} diff --git a/Teknik/Areas/User/Scripts/Settings.js b/Teknik/Areas/User/Scripts/Settings.js new file mode 100644 index 0000000..64976eb --- /dev/null +++ b/Teknik/Areas/User/Scripts/Settings.js @@ -0,0 +1,22 @@ +$(document).ready(function () { + $('#delete_account').click(function () { + bootbox.confirm("Are you sure you want to delete your account?", function (result) { + if (result) { + $.ajax({ + type: "POST", + url: deleteUserURL, + data: AddAntiForgeryToken({}), + success: function (html) { + if (html.result) { + window.location.replace(homeUrl); + } + else { + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
    ' + parseErrorMessage(html) + '
    '); + } + } + }); + } + }); + }); +}); diff --git a/Teknik/Areas/User/Scripts/UploadSettings.js b/Teknik/Areas/User/Scripts/UploadSettings.js new file mode 100644 index 0000000..36abf06 --- /dev/null +++ b/Teknik/Areas/User/Scripts/UploadSettings.js @@ -0,0 +1,29 @@ +$(document).ready(function () { + $("[name='update_upload_encrypt']").bootstrapSwitch(); + + $("#update_submit").click(function () { + // Start Updating Animation + $.blockUI({ message: '

    Updating...

    ' }); + + upload_encrypt = $("#update_upload_encrypt").is(":checked"); + $.ajax({ + type: "POST", + url: editURL, + data: AddAntiForgeryToken({ + Encrypt: upload_encrypt + }), + success: function (html) { + $.unblockUI(); + if (html.result) { + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
    Settings Saved!
    '); + } + else { + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
    ' + parseErrorMessage(html) + '
    '); + } + } + }); + return false; + }); +}); diff --git a/Teknik/Areas/User/UserAreaRegistration.cs b/Teknik/Areas/User/UserAreaRegistration.cs index 3f30a98..a36a440 100644 --- a/Teknik/Areas/User/UserAreaRegistration.cs +++ b/Teknik/Areas/User/UserAreaRegistration.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Web.Mvc; using System.Web.Optimization; using Teknik.Configuration; @@ -59,6 +59,46 @@ namespace Teknik.Areas.Users new { controller = "User", action = "Settings" }, // Parameter defaults new[] { typeof(Controllers.UserController).Namespace } ); + context.MapSubdomainRoute( + "User.SecuritySettings", // Route name + new List() { "user" }, // Subdomains + new List() { config.Host }, // domains + "Settings/Security", // URL with parameters + new { controller = "User", action = "SecuritySettings" }, // Parameter defaults + new[] { typeof(Controllers.UserController).Namespace } + ); + context.MapSubdomainRoute( + "User.ProfileSettings", // Route name + new List() { "user" }, // Subdomains + new List() { config.Host }, // domains + "Settings/Profile", // URL with parameters + new { controller = "User", action = "ProfileSettings" }, // Parameter defaults + new[] { typeof(Controllers.UserController).Namespace } + ); + context.MapSubdomainRoute( + "User.InviteSettings", // Route name + new List() { "user" }, // Subdomains + new List() { config.Host }, // domains + "Settings/Invites", // URL with parameters + new { controller = "User", action = "InviteSettings" }, // Parameter defaults + new[] { typeof(Controllers.UserController).Namespace } + ); + context.MapSubdomainRoute( + "User.BlogSettings", // Route name + new List() { "user" }, // Subdomains + new List() { config.Host }, // domains + "Settings/Blog", // URL with parameters + new { controller = "User", action = "BlogSettings" }, // Parameter defaults + new[] { typeof(Controllers.UserController).Namespace } + ); + context.MapSubdomainRoute( + "User.UploadSettings", // Route name + new List() { "user" }, // Subdomains + new List() { config.Host }, // domains + "Settings/Uploads", // URL with parameters + new { controller = "User", action = "UploadSettings" }, // Parameter defaults + new[] { typeof(Controllers.UserController).Namespace } + ); context.MapSubdomainRoute( "User.ResetPassword", // Route name new List() { "user" }, // Subdomains @@ -123,11 +163,37 @@ namespace Teknik.Areas.Users "~/Scripts/bootstrap-switch.js", "~/Areas/User/Scripts/User.js")); - // Register Script Bundle + BundleTable.Bundles.Add(new CdnScriptBundle("~/bundles/user/reset", config.CdnHost).Include( + "~/Areas/User/Scripts/ResetPass.js")); + + BundleTable.Bundles.Add(new CdnScriptBundle("~/bundles/user/settings", config.CdnHost).Include( + "~/Scripts/bootbox/bootbox.min.js", + "~/Areas/User/Scripts/Settings.js")); + + BundleTable.Bundles.Add(new CdnScriptBundle("~/bundles/user/settings/blog", config.CdnHost).Include( + "~/Scripts/jquery.blockUI.js", + "~/Areas/User/Scripts/BlogSettings.js")); + + BundleTable.Bundles.Add(new CdnScriptBundle("~/bundles/user/settings/invite", config.CdnHost).Include( + "~/Areas/User/Scripts/InviteSettings.js")); + + BundleTable.Bundles.Add(new CdnScriptBundle("~/bundles/user/settings/profile", config.CdnHost).Include( + "~/Scripts/jquery.blockUI.js", + "~/Areas/User/Scripts/ProfileSettings.js")); + + BundleTable.Bundles.Add(new CdnScriptBundle("~/bundles/user/settings/security", config.CdnHost).Include( + "~/Scripts/jquery.blockUI.js", + "~/Scripts/bootstrap-switch.js", + "~/Areas/User/Scripts/SecuritySettings.js")); + + BundleTable.Bundles.Add(new CdnScriptBundle("~/bundles/user/settings/upload", config.CdnHost).Include( + "~/Scripts/jquery.blockUI.js", + "~/Scripts/bootstrap-switch.js", + "~/Areas/User/Scripts/UploadSettings.js")); + BundleTable.Bundles.Add(new CdnScriptBundle("~/bundles/checkAuthCode", config.CdnHost).Include( "~/Areas/User/Scripts/CheckAuthCode.js")); - - // Register Script Bundle + BundleTable.Bundles.Add(new CdnScriptBundle("~/bundles/profile", config.CdnHost).Include( "~/Scripts/bootbox/bootbox.min.js", "~/Areas/User/Scripts/Profile.js")); @@ -135,6 +201,12 @@ namespace Teknik.Areas.Users // Register Style Bundles BundleTable.Bundles.Add(new CdnStyleBundle("~/Content/user", config.CdnHost).Include( "~/Content/bootstrap-switch/bootstrap3/bootstrap-switch.css")); + + BundleTable.Bundles.Add(new CdnStyleBundle("~/Content/user/settings/security", config.CdnHost).Include( + "~/Content/bootstrap-switch/bootstrap3/bootstrap-switch.css")); + + BundleTable.Bundles.Add(new CdnStyleBundle("~/Content/user/settings/upload", config.CdnHost).Include( + "~/Content/bootstrap-switch/bootstrap3/bootstrap-switch.css")); } } -} \ No newline at end of file +} diff --git a/Teknik/Areas/User/Utility/UserHelper.cs b/Teknik/Areas/User/Utility/UserHelper.cs index 266fedb..2c30a3b 100644 --- a/Teknik/Areas/User/Utility/UserHelper.cs +++ b/Teknik/Areas/User/Utility/UserHelper.cs @@ -186,6 +186,11 @@ namespace Teknik.Areas.Users.Utility } } + public static void EditAccount(TeknikEntities db, Config config, User user) + { + EditAccount(db, config, user, false, string.Empty); + } + public static void EditAccount(TeknikEntities db, Config config, User user, bool changePass, string password) { try @@ -656,6 +661,34 @@ namespace Teknik.Areas.Users.Utility db.SaveChanges(); } + // Delete Owned Invite Codes + if (user.OwnedInviteCodes != null) + { + foreach (InviteCode code in user.OwnedInviteCodes) + { + db.InviteCodes.Remove(code); + } + db.SaveChanges(); + } + + // Delete Claimed Invite Code + if (user.ClaimedInviteCode != null) + { + db.InviteCodes.Remove(user.ClaimedInviteCode); + db.SaveChanges(); + } + + // Delete Auth Tokens + List authTokens = db.AuthTokens.Where(t => t.User.UserId == user.UserId).ToList(); + if (authTokens != null) + { + foreach (AuthToken authToken in authTokens) + { + db.AuthTokens.Remove(authToken); + } + db.SaveChanges(); + } + // Delete User db.Users.Remove(user); db.SaveChanges(); diff --git a/Teknik/Areas/User/ViewModels/BlogSettingsViewModel.cs b/Teknik/Areas/User/ViewModels/BlogSettingsViewModel.cs new file mode 100644 index 0000000..46d8450 --- /dev/null +++ b/Teknik/Areas/User/ViewModels/BlogSettingsViewModel.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using Teknik.Areas.Users.Models; +using Teknik.ViewModels; + +namespace Teknik.Areas.Users.ViewModels +{ + public class BlogSettingsViewModel : SettingsViewModel + { + public string Title { get; set; } + + public string Description { get; set; } + + public BlogSettingsViewModel() + { + Title = string.Empty; + Description = string.Empty; + } + } +} diff --git a/Teknik/Areas/User/ViewModels/InviteCodeViewModel.cs b/Teknik/Areas/User/ViewModels/InviteCodeViewModel.cs new file mode 100644 index 0000000..86ce983 --- /dev/null +++ b/Teknik/Areas/User/ViewModels/InviteCodeViewModel.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using Teknik.ViewModels; + +namespace Teknik.Areas.Users.ViewModels +{ + public class InviteCodeViewModel : ViewModelBase + { + public int InviteCodeId { get; set; } + + public bool Active { get; set; } + + public string Code { get; set; } + + public virtual Models.User Owner { get; set; } + + public virtual Models.User ClaimedUser { get; set; } + } +} diff --git a/Teknik/Areas/User/ViewModels/InviteSettingsViewModel.cs b/Teknik/Areas/User/ViewModels/InviteSettingsViewModel.cs new file mode 100644 index 0000000..0e02469 --- /dev/null +++ b/Teknik/Areas/User/ViewModels/InviteSettingsViewModel.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using Teknik.Areas.Users.Models; +using Teknik.ViewModels; + +namespace Teknik.Areas.Users.ViewModels +{ + public class InviteSettingsViewModel : SettingsViewModel + { + public List AvailableCodes { get; set; } + public List ClaimedCodes { get; set; } + + public InviteSettingsViewModel() + { + AvailableCodes = new List(); + ClaimedCodes = new List(); + } + } +} diff --git a/Teknik/Areas/User/ViewModels/ProfileSettingsViewModel.cs b/Teknik/Areas/User/ViewModels/ProfileSettingsViewModel.cs new file mode 100644 index 0000000..99292aa --- /dev/null +++ b/Teknik/Areas/User/ViewModels/ProfileSettingsViewModel.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using Teknik.Areas.Users.Models; +using Teknik.ViewModels; + +namespace Teknik.Areas.Users.ViewModels +{ + public class ProfileSettingsViewModel : SettingsViewModel + { + [AllowHtml] + public string About { get; set; } + + public string Website { get; set; } + + public string Quote { get; set; } + + public ProfileSettingsViewModel() + { + About = string.Empty; + Website = string.Empty; + Quote = string.Empty; + } + } +} diff --git a/Teknik/Areas/User/ViewModels/RegisterViewModel.cs b/Teknik/Areas/User/ViewModels/RegisterViewModel.cs index cd97fda..c50017f 100644 --- a/Teknik/Areas/User/ViewModels/RegisterViewModel.cs +++ b/Teknik/Areas/User/ViewModels/RegisterViewModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel.DataAnnotations; using Teknik.Areas.Users.Models; using Teknik.Utilities; @@ -24,6 +24,9 @@ namespace Teknik.Areas.Users.ViewModels [DataType(DataType.Password)] public string ConfirmPassword { get; set; } + [Display(Name = "InviteCode")] + public string InviteCode { get; set; } + [Display(Name = "Recovery Email")] [DataType(DataType.EmailAddress)] public string RecoveryEmail { get; set; } @@ -34,4 +37,4 @@ namespace Teknik.Areas.Users.ViewModels public string ReturnUrl { get; set; } } -} \ No newline at end of file +} diff --git a/Teknik/Areas/User/ViewModels/SecuritySettingsViewModel.cs b/Teknik/Areas/User/ViewModels/SecuritySettingsViewModel.cs new file mode 100644 index 0000000..006ddc6 --- /dev/null +++ b/Teknik/Areas/User/ViewModels/SecuritySettingsViewModel.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using Teknik.Areas.Users.Models; +using Teknik.ViewModels; + +namespace Teknik.Areas.Users.ViewModels +{ + public class SecuritySettingsViewModel : SettingsViewModel + { + [AllowHtml] + public string CurrentPassword { get; set; } + + [AllowHtml] + public string NewPassword { get; set; } + + [AllowHtml] + public string NewPasswordConfirm { get; set; } + + public string PgpPublicKey { get; set; } + + public string RecoveryEmail { get; set; } + + public bool RecoveryVerified { get; set; } + + public bool AllowTrustedDevices { get; set; } + + public bool TwoFactorEnabled { get; set; } + + public string TwoFactorKey { get; set; } + + public int TrustedDeviceCount { get; set; } + + public List AuthTokens { get; set; } + + public SecuritySettingsViewModel() + { + TrustedDeviceCount = 0; + AuthTokens = new List(); + RecoveryEmail = string.Empty; + RecoveryVerified = false; + AllowTrustedDevices = false; + TwoFactorEnabled = false; + TwoFactorKey = string.Empty; + PgpPublicKey = string.Empty; + } + } +} diff --git a/Teknik/Areas/User/ViewModels/SettingsViewModel.cs b/Teknik/Areas/User/ViewModels/SettingsViewModel.cs index 3b70a16..fcde639 100644 --- a/Teknik/Areas/User/ViewModels/SettingsViewModel.cs +++ b/Teknik/Areas/User/ViewModels/SettingsViewModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Web; @@ -13,16 +13,6 @@ namespace Teknik.Areas.Users.ViewModels public string Username { get; set; } - public int TrustedDeviceCount { get; set; } - - public List AuthTokens { get; set; } - - public UserSettings UserSettings { get; set; } - - public SecuritySettings SecuritySettings { get; set; } - - public BlogSettings BlogSettings { get; set; } - - public UploadSettings UploadSettings { get; set; } + public string Page { get; set; } } -} \ No newline at end of file +} diff --git a/Teknik/Areas/User/ViewModels/UploadSettingsViewModel.cs b/Teknik/Areas/User/ViewModels/UploadSettingsViewModel.cs new file mode 100644 index 0000000..4273bb2 --- /dev/null +++ b/Teknik/Areas/User/ViewModels/UploadSettingsViewModel.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using Teknik.Areas.Users.Models; +using Teknik.ViewModels; + +namespace Teknik.Areas.Users.ViewModels +{ + public class UploadSettingsViewModel : SettingsViewModel + { + public bool Encrypt { get; set; } + + public UploadSettingsViewModel() + { + Encrypt = false; + } + } +} diff --git a/Teknik/Areas/User/Views/User/Register.cshtml b/Teknik/Areas/User/Views/User/Register.cshtml index c79476c..0753d9c 100644 --- a/Teknik/Areas/User/Views/User/Register.cshtml +++ b/Teknik/Areas/User/Views/User/Register.cshtml @@ -1,4 +1,4 @@ -@model Teknik.Areas.Users.ViewModels.RegisterViewModel +@model Teknik.Areas.Users.ViewModels.RegisterViewModel @using Teknik.Utilities @@ -17,19 +17,30 @@
    - + +
    - + +
    - + +
    - + +
    - + + +
    +
    + +

    @@ -48,4 +59,4 @@ else {

    Registration has been disabled

    -} \ No newline at end of file +} diff --git a/Teknik/Areas/User/Views/User/ResetPassword.cshtml b/Teknik/Areas/User/Views/User/ResetPassword.cshtml index 4fb3462..7b97701 100644 --- a/Teknik/Areas/User/Views/User/ResetPassword.cshtml +++ b/Teknik/Areas/User/Views/User/ResetPassword.cshtml @@ -1,8 +1,8 @@ -@model Teknik.Areas.Users.ViewModels.ResetPasswordViewModel +@model Teknik.Areas.Users.ViewModels.ResetPasswordViewModel @using Teknik.Utilities -@Scripts.Render("~/bundles/user") +@Scripts.Render("~/bundles/user/reset")
    @@ -31,4 +31,4 @@
    - \ No newline at end of file + diff --git a/Teknik/Areas/User/Views/User/ResetPasswordVerification.cshtml b/Teknik/Areas/User/Views/User/ResetPasswordVerification.cshtml index 53fd253..5300238 100644 --- a/Teknik/Areas/User/Views/User/ResetPasswordVerification.cshtml +++ b/Teknik/Areas/User/Views/User/ResetPasswordVerification.cshtml @@ -1,8 +1,8 @@ -@model Teknik.Areas.Users.ViewModels.ResetPasswordVerificationViewModel +@model Teknik.Areas.Users.ViewModels.ResetPasswordVerificationViewModel @using Teknik.Utilities -@Scripts.Render("~/bundles/user") +@Scripts.Render("~/bundles/user/reset")
    diff --git a/Teknik/Areas/User/Views/User/Settings.cshtml b/Teknik/Areas/User/Views/User/Settings.cshtml deleted file mode 100644 index c4dc12a..0000000 --- a/Teknik/Areas/User/Views/User/Settings.cshtml +++ /dev/null @@ -1,255 +0,0 @@ -@model Teknik.Areas.Users.ViewModels.SettingsViewModel - -@using Teknik.Utilities -@using Teknik.Areas.Users.ViewModels - - - -@Styles.Render("~/Content/user") -@Scripts.Render("~/bundles/user") - -
    - @if (!Model.Error) - { - -
    -
    -
    - - - -
    - -
    -
    -
    - - -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    -
    -
    - - - @if (!string.IsNullOrEmpty(Model.SecuritySettings.RecoveryEmail)) - { -

    - @if (Model.SecuritySettings.RecoveryVerified) - { - Verified - } - else - { - Unverified Resend - } -

    - } -
    -
    -
    -
    - -
    - -
    - -
    -
    -
    -
    - -
    - -
    - -
    -
    -
    -
    -
    - -
    -
      - @if (Model.AuthTokens.Any()) - { - foreach (AuthTokenViewModel token in Model.AuthTokens) - { - @Html.Partial("AuthToken", token) - } - } - else - { -
    • No Authentication Tokens
    • - } -
    -
    -
    -
    -
    - -
    -
    -
    - - -
    -
    - - -
    -
    -
    - -
    -
    -
    -
    - -
    -
    -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - -
    -
    -
    - -
    -
    - } - else - { -
    -
    -

    Sorry, but I couldn't find that user.

    -
    -
    - } -
    diff --git a/Teknik/Areas/User/Views/User/AuthToken.cshtml b/Teknik/Areas/User/Views/User/Settings/AuthToken.cshtml similarity index 100% rename from Teknik/Areas/User/Views/User/AuthToken.cshtml rename to Teknik/Areas/User/Views/User/Settings/AuthToken.cshtml diff --git a/Teknik/Areas/User/Views/User/Settings/BlogSettings.cshtml b/Teknik/Areas/User/Views/User/Settings/BlogSettings.cshtml new file mode 100644 index 0000000..9b6c4a6 --- /dev/null +++ b/Teknik/Areas/User/Views/User/Settings/BlogSettings.cshtml @@ -0,0 +1,36 @@ +@model Teknik.Areas.Users.ViewModels.BlogSettingsViewModel + +@using Teknik.Utilities +@using Teknik.Areas.Users.ViewModels + +@{ + Layout = "~/Areas/User/Views/User/Settings/Settings.cshtml"; +} + + + +@Styles.Render("~/Content/user/settings/blog") +@Scripts.Render("~/bundles/user/settings/blog") + + +
    +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    diff --git a/Teknik/Areas/User/Views/User/Settings/InviteCode.cshtml b/Teknik/Areas/User/Views/User/Settings/InviteCode.cshtml new file mode 100644 index 0000000..c31c186 --- /dev/null +++ b/Teknik/Areas/User/Views/User/Settings/InviteCode.cshtml @@ -0,0 +1,21 @@ +@using Teknik.Utilities +@model Teknik.Areas.Users.ViewModels.InviteCodeViewModel + +@{ + bool codeClaimed = Model.ClaimedUser != null; +} + +
  • + @if (!codeClaimed) + { +
    + + +
    + } +

    @Model.Code

    + @if (codeClaimed) + { +

    Claimed by @Model.ClaimedUser.Username

    + } +
  • diff --git a/Teknik/Areas/User/Views/User/Settings/InviteSettings.cshtml b/Teknik/Areas/User/Views/User/Settings/InviteSettings.cshtml new file mode 100644 index 0000000..eb17061 --- /dev/null +++ b/Teknik/Areas/User/Views/User/Settings/InviteSettings.cshtml @@ -0,0 +1,59 @@ +@model Teknik.Areas.Users.ViewModels.InviteSettingsViewModel + +@using Teknik.Utilities +@using Teknik.Areas.Users.ViewModels + +@{ + Layout = "~/Areas/User/Views/User/Settings/Settings.cshtml"; +} + +@Styles.Render("~/Content/user/settings/invite") +@Scripts.Render("~/bundles/user/settings/invite") + + + +
    +
    +
    + +
    +
      + @if (Model.AvailableCodes.Any()) + { + foreach (InviteCodeViewModel code in Model.AvailableCodes) + { + @Html.Partial("Settings/InviteCode", code) + } + } + else + { +
    • No Invite Codes Available
    • + } +
    +
    +
    +
    + +
    +
    +
    + +
    +
      + @if (Model.ClaimedCodes.Any()) + { + foreach (InviteCodeViewModel code in Model.ClaimedCodes) + { + @Html.Partial("Settings/InviteCode", code) + } + } + else + { +
    • No Invite Codes have been Claimed
    • + } +
    +
    +
    +
    diff --git a/Teknik/Areas/User/Views/User/Settings/ProfileSettings.cshtml b/Teknik/Areas/User/Views/User/Settings/ProfileSettings.cshtml new file mode 100644 index 0000000..1947ab0 --- /dev/null +++ b/Teknik/Areas/User/Views/User/Settings/ProfileSettings.cshtml @@ -0,0 +1,41 @@ +@model Teknik.Areas.Users.ViewModels.ProfileSettingsViewModel + +@using Teknik.Utilities +@using Teknik.Areas.Users.ViewModels + +@{ + Layout = "~/Areas/User/Views/User/Settings/Settings.cshtml"; +} + + + +@Styles.Render("~/Content/user/settings/profile") +@Scripts.Render("~/bundles/user/settings/profile") + +
    +
    +
    + + +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    diff --git a/Teknik/Areas/User/Views/User/Settings/SecuritySettings.cshtml b/Teknik/Areas/User/Views/User/Settings/SecuritySettings.cshtml new file mode 100644 index 0000000..bff5876 --- /dev/null +++ b/Teknik/Areas/User/Views/User/Settings/SecuritySettings.cshtml @@ -0,0 +1,178 @@ +@model Teknik.Areas.Users.ViewModels.SecuritySettingsViewModel + +@using Teknik.Utilities +@using Teknik.Areas.Users.ViewModels + +@{ + Layout = "~/Areas/User/Views/User/Settings/Settings.cshtml"; +} + + + +@Styles.Render("~/Content/user/settings/security") +@Scripts.Render("~/bundles/user/settings/security") + + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    + + + @if (!string.IsNullOrEmpty(Model.RecoveryEmail)) + { +

    + @if (Model.RecoveryVerified) + { + Verified + } + else + { + Unverified Resend + } +

    + } +
    +
    +
    +
    + +
    + +
    + +
    +
    +
    +
    + +
    + +
    + +
    +
    +
    +
    +
    + +
    +
      + @if (Model.AuthTokens.Any()) + { + foreach (AuthTokenViewModel token in Model.AuthTokens) + { + @Html.Partial("Settings/AuthToken", token) + } + } + else + { +
    • No Authentication Tokens
    • + } +
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    diff --git a/Teknik/Areas/User/Views/User/Settings/Settings.cshtml b/Teknik/Areas/User/Views/User/Settings/Settings.cshtml new file mode 100644 index 0000000..b93c952 --- /dev/null +++ b/Teknik/Areas/User/Views/User/Settings/Settings.cshtml @@ -0,0 +1,51 @@ +@model Teknik.Areas.Users.ViewModels.SettingsViewModel + +@{ + Layout = "~/Views/Shared/_Layout.cshtml"; +} + +@using Teknik.Utilities +@using Teknik.Areas.Users.ViewModels + +@Styles.Render("~/Content/user/settings") +@Scripts.Render("~/bundles/user/settings") + + + +
    + @if (!Model.Error) + { +
    + +
    + @RenderBody() +
    +
    + } + else + { +
    +
    +

    @Model.ErrorMessage

    +
    +
    + } +
    diff --git a/Teknik/Areas/User/Views/User/Settings/SettingsBase.cshtml b/Teknik/Areas/User/Views/User/Settings/SettingsBase.cshtml new file mode 100644 index 0000000..6325730 --- /dev/null +++ b/Teknik/Areas/User/Views/User/Settings/SettingsBase.cshtml @@ -0,0 +1,12 @@ +@model Teknik.Areas.Users.ViewModels.SettingsViewModel + +@{ + Layout = "~/Areas/User/Views/User/Settings/Settings.cshtml"; +} + +
    +
    +

    Default Account Settings

    +
    +
    + diff --git a/Teknik/Areas/User/Views/User/Settings/UploadSettings.cshtml b/Teknik/Areas/User/Views/User/Settings/UploadSettings.cshtml new file mode 100644 index 0000000..58d403d --- /dev/null +++ b/Teknik/Areas/User/Views/User/Settings/UploadSettings.cshtml @@ -0,0 +1,36 @@ +@model Teknik.Areas.Users.ViewModels.UploadSettingsViewModel + +@using Teknik.Utilities +@using Teknik.Areas.Users.ViewModels + +@{ + Layout = "~/Areas/User/Views/User/Settings/Settings.cshtml"; +} + + + +@Styles.Render("~/Content/user/settings/upload") +@Scripts.Render("~/bundles/user/settings/upload") + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + +
    +
    +
    diff --git a/Teknik/Areas/User/Views/User/_LoginPartial.cshtml b/Teknik/Areas/User/Views/User/_LoginPartial.cshtml index 2ed4e48..79b2d8a 100644 --- a/Teknik/Areas/User/Views/User/_LoginPartial.cshtml +++ b/Teknik/Areas/User/Views/User/_LoginPartial.cshtml @@ -1,11 +1,11 @@ -@model Teknik.ViewModels.ViewModelBase +@model Teknik.ViewModels.ViewModelBase @using Teknik.Utilities @using Microsoft.AspNet.Identity @if (Model.Config.UserConfig.RegistrationEnabled || Model.Config.UserConfig.LoginEnabled) { - +} diff --git a/Teknik/Models/TeknikEntities.cs b/Teknik/Models/TeknikEntities.cs index e82035a..7b21a53 100644 --- a/Teknik/Models/TeknikEntities.cs +++ b/Teknik/Models/TeknikEntities.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNet.Identity.EntityFramework; +using Microsoft.AspNet.Identity.EntityFramework; using System.Data.Entity; using System.Data.Entity.Infrastructure; using Teknik.Areas.Blog.Models; @@ -26,6 +26,7 @@ namespace Teknik.Models public DbSet TrustedDevices { get; set; } public DbSet AuthTokens { get; set; } public DbSet TransferTypes { get; set; } + public DbSet InviteCodes { get; set; } // User Settings public DbSet UserSettings { get; set; } public DbSet SecuritySettings { get; set; } @@ -115,6 +116,16 @@ namespace Teknik.Models .WithOptional(u => u.User) .WillCascadeOnDelete(false); + modelBuilder.Entity() + .HasMany(u => u.OwnedInviteCodes) + .WithOptional(c => c.Owner) + .WillCascadeOnDelete(false); + + modelBuilder.Entity() + .HasOptional(u => u.ClaimedInviteCode) + .WithOptionalPrincipal(c => c.ClaimedUser) + .WillCascadeOnDelete(false); + // Upload Mappings modelBuilder.Entity() .HasOptional(u => u.User); @@ -130,13 +141,14 @@ namespace Teknik.Models // Vault Mappings modelBuilder.Entity() .HasOptional(u => u.User); - + // Users modelBuilder.Entity().ToTable("Users"); modelBuilder.Entity().ToTable("Groups"); modelBuilder.Entity().ToTable("Roles"); modelBuilder.Entity().ToTable("TrustedDevices"); modelBuilder.Entity().ToTable("AuthTokens"); + modelBuilder.Entity().ToTable("InviteCodes"); modelBuilder.Entity().ToTable("TransferTypes"); modelBuilder.Entity().ToTable("RecoveryEmailVerifications"); modelBuilder.Entity().ToTable("ResetPasswordVerifications"); @@ -176,4 +188,4 @@ namespace Teknik.Models base.OnModelCreating(modelBuilder); } } -} \ No newline at end of file +} diff --git a/Teknik/Scripts/common.js b/Teknik/Scripts/common.js index 4a6c372..96b16d0 100644 --- a/Teknik/Scripts/common.js +++ b/Teknik/Scripts/common.js @@ -1,4 +1,4 @@ -$(document).ready(function () { +$(document).ready(function () { $("#top_msg").css('display', 'none', 'important'); // Opt-in for tooltips @@ -280,6 +280,41 @@ function addParamsToUrl(origUrl, params) { var appendix = (hasQuery ? "&" : "?") + paramStr; return hasHash ? origUrl.replace("#", appendix + "#") : origUrl + appendix; } + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +function parseErrorMessage(errorObj) { + var errorMsg = ""; + if (errorObj === null || errorObj === undefined) { + // Do nothing + } + else if (errorObj.constructor === {}.constructor) { + if (errorObj.error) { + errorMsg = errorObj.error; + if (errorObj.error.message) { + errorMsg = errorObj.error.message; + } + } + } + else { + try { + var json = $.parseJSON(errorObj); + if (json.error) { + errorMsg = json.error; + if (json.error.message) { + errorMsg = json.error.message; + } + } + } + catch (err) { + // Don't do anything + } + } + return errorMsg; +} + /***************************** TIMER Page Load *******************************/ var loopTime; var startTime = new Date(); @@ -301,4 +336,4 @@ function pageloadStopTimer() { $('#pagetime').show(); clearTimeout(loopTime); -} \ No newline at end of file +} diff --git a/Teknik/Teknik.csproj b/Teknik/Teknik.csproj index ef2f802..fc86dd3 100644 --- a/Teknik/Teknik.csproj +++ b/Teknik/Teknik.csproj @@ -293,15 +293,22 @@ + + + + + + + @@ -410,7 +417,13 @@ - + + + + + + + @@ -598,7 +611,7 @@ - + @@ -643,7 +656,7 @@ - + @@ -666,6 +679,13 @@ + + + + + + + diff --git a/Utilities/Configuration/UserConfig.cs b/Utilities/Configuration/UserConfig.cs index 17299e2..60a602f 100644 --- a/Utilities/Configuration/UserConfig.cs +++ b/Utilities/Configuration/UserConfig.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -19,6 +19,7 @@ namespace Teknik.Configuration public string ReservedUsernameDefinitionFile { get; set; } public decimal PremiumAccountPrice { get; set; } public string PaymentType { get; set; } + public bool InviteCodeRequired { get; set; } public UserConfig() { @@ -32,6 +33,7 @@ namespace Teknik.Configuration ReservedUsernameDefinitionFile = string.Empty; PremiumAccountPrice = 0; PaymentType = "Donation"; + InviteCodeRequired = false; } } }