Browse Source

- Implemented invite codes to use for registration.

- Reorganized settings pages into individual pages.
tags/3.0.0
Teknikode 1 year ago
parent
commit
a7c314d728
41 changed files with 1421 additions and 457 deletions
  1. 19
    0
      Teknik/Areas/Admin/Controllers/AdminController.cs
  2. 28
    10
      Teknik/Areas/Admin/Scripts/UserInfo.js
  3. 7
    0
      Teknik/Areas/Admin/Views/Admin/UserInfo.cshtml
  4. 301
    29
      Teknik/Areas/User/Controllers/UserController.cs
  5. 27
    0
      Teknik/Areas/User/Models/InviteCode.cs
  6. 6
    0
      Teknik/Areas/User/Models/User.cs
  7. 29
    0
      Teknik/Areas/User/Scripts/BlogSettings.js
  8. 35
    0
      Teknik/Areas/User/Scripts/InviteSettings.js
  9. 31
    0
      Teknik/Areas/User/Scripts/ProfileSettings.js
  10. 49
    0
      Teknik/Areas/User/Scripts/ResetPass.js
  11. 18
    115
      Teknik/Areas/User/Scripts/SecuritySettings.js
  12. 22
    0
      Teknik/Areas/User/Scripts/Settings.js
  13. 29
    0
      Teknik/Areas/User/Scripts/UploadSettings.js
  14. 77
    5
      Teknik/Areas/User/UserAreaRegistration.cs
  15. 33
    0
      Teknik/Areas/User/Utility/UserHelper.cs
  16. 22
    0
      Teknik/Areas/User/ViewModels/BlogSettingsViewModel.cs
  17. 21
    0
      Teknik/Areas/User/ViewModels/InviteCodeViewModel.cs
  18. 21
    0
      Teknik/Areas/User/ViewModels/InviteSettingsViewModel.cs
  19. 27
    0
      Teknik/Areas/User/ViewModels/ProfileSettingsViewModel.cs
  20. 5
    2
      Teknik/Areas/User/ViewModels/RegisterViewModel.cs
  21. 50
    0
      Teknik/Areas/User/ViewModels/SecuritySettingsViewModel.cs
  22. 3
    13
      Teknik/Areas/User/ViewModels/SettingsViewModel.cs
  23. 19
    0
      Teknik/Areas/User/ViewModels/UploadSettingsViewModel.cs
  24. 18
    7
      Teknik/Areas/User/Views/User/Register.cshtml
  25. 3
    3
      Teknik/Areas/User/Views/User/ResetPassword.cshtml
  26. 2
    2
      Teknik/Areas/User/Views/User/ResetPasswordVerification.cshtml
  27. 0
    255
      Teknik/Areas/User/Views/User/Settings.cshtml
  28. 0
    0
      Teknik/Areas/User/Views/User/Settings/AuthToken.cshtml
  29. 36
    0
      Teknik/Areas/User/Views/User/Settings/BlogSettings.cshtml
  30. 21
    0
      Teknik/Areas/User/Views/User/Settings/InviteCode.cshtml
  31. 59
    0
      Teknik/Areas/User/Views/User/Settings/InviteSettings.cshtml
  32. 41
    0
      Teknik/Areas/User/Views/User/Settings/ProfileSettings.cshtml
  33. 178
    0
      Teknik/Areas/User/Views/User/Settings/SecuritySettings.cshtml
  34. 51
    0
      Teknik/Areas/User/Views/User/Settings/Settings.cshtml
  35. 12
    0
      Teknik/Areas/User/Views/User/Settings/SettingsBase.cshtml
  36. 36
    0
      Teknik/Areas/User/Views/User/Settings/UploadSettings.cshtml
  37. 7
    7
      Teknik/Areas/User/Views/User/_LoginPartial.cshtml
  38. 15
    3
      Teknik/Models/TeknikEntities.cs
  39. 37
    2
      Teknik/Scripts/common.js
  40. 23
    3
      Teknik/Teknik.csproj
  41. 3
    1
      Utilities/Configuration/UserConfig.cs

+ 19
- 0
Teknik/Areas/Admin/Controllers/AdminController.cs View File

@@ -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"));
}
}
}

+ 28
- 10
Teknik/Areas/Admin/Scripts/UserInfo.js View File

@@ -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('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + html.error.message + '</div>');
}
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('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(html) + '</div>');
}
}
}
});
@@ -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('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + html.error.message + '</div>');
}
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('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(html) + '</div>');
}
}
}
});
});

$('#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('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(html) + '</div>');
}
}
});
});
});

+ 7
- 0
Teknik/Areas/Admin/Views/Admin/UserInfo.cshtml View File

@@ -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';
</script>

@@ -52,4 +53,10 @@
</select>
</div>
</div>
<br />
<div class="row">
<div class="col-sm-2 col-sm-offset-1">
<button type="button" class="list-group-item btn-info" id="createInviteCode">Create Invite Code</button>
</div>
</div>
</div>

+ 301
- 29
Teknik/Areas/User/Controllers/UserController.cs View File

@@ -102,23 +102,56 @@ namespace Teknik.Areas.Users.Controllers
[TrackPageView]
public ActionResult Settings()
{
string username = User.Identity.Name;
return Redirect(Url.SubRouteUrl("user", "User.SecuritySettings"));
}

SettingsViewModel model = new SettingsViewModel();
ViewBag.Title = "User Does Not Exist - " + Config.Title;
ViewBag.Description = "The User does not exist";
[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 = "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 = "Settings - " + Config.Title;
ViewBag.Description = "Your " + Config.Title + " Settings";
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<InviteCodeViewModel> availableCodes = new List<InviteCodeViewModel>();
List<InviteCodeViewModel> claimedCodes = new List<InviteCodeViewModel>();
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) });
}
}
}
}

+ 27
- 0
Teknik/Areas/User/Models/InviteCode.cs View File

@@ -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;
}
}
}

+ 6
- 0
Teknik/Areas/User/Models/User.cs View File

@@ -24,6 +24,10 @@ namespace Teknik.Areas.Users.Models

public DateTime LastSeen { get; set; }

public virtual InviteCode ClaimedInviteCode { get; set; }

public virtual ICollection<InviteCode> 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<Group>();
TrustedDevices = new List<TrustedDevice>();
AuthTokens = new List<AuthToken>();
ClaimedInviteCode = null;
OwnedInviteCodes = new List<InviteCode>();
}
}
}

+ 29
- 0
Teknik/Areas/User/Scripts/BlogSettings.js View File

@@ -0,0 +1,29 @@
$(document).ready(function () {
$("#update_submit").click(function () {
// Start Updating Animation
$.blockUI({ message: '<div class="text-center"><h3>Updating...</h3></div>' });
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('<div class="alert alert-success alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>Settings Saved!</div>');
}
else {
$("#top_msg").css('display', 'inline', 'important');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(html) + '</div>');
}
}
});
return false;
});
});

+ 35
- 0
Teknik/Areas/User/Scripts/InviteSettings.js View File

@@ -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: '<input type="text" class="form-control" id="inviteCodeLink" onClick="this.select();" value="' + response.result + '">'
});
}
else {
$("#top_msg").css('display', 'inline', 'important');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(response) + '</div>');
}
}
});
}

+ 31
- 0
Teknik/Areas/User/Scripts/ProfileSettings.js View File

@@ -0,0 +1,31 @@
$(document).ready(function () {
$("#update_submit").click(function () {
// Start Updating Animation
$.blockUI({ message: '<div class="text-center"><h3>Updating...</h3></div>' });
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('<div class="alert alert-success alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>Settings Saved!</div>');
}
else {
$("#top_msg").css('display', 'inline', 'important');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(html) + '</div>');
}
}
});
return false;
});
});

+ 49
- 0
Teknik/Areas/User/Scripts/ResetPass.js View File

@@ -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('<div class="alert alert-success alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>The Password Reset Link has been sent to your recovery email.</div>');
}
else {
$("#top_msg").css('display', 'inline', 'important');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(html) + '</div>');
}
}
});
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('<div class="alert alert-success alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>Password has successfully been reset.</div>');
}
else {
$("#top_msg").css('display', 'inline', 'important');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(html) + '</div>');
}
}
});
return false;
});
});

Teknik/Areas/User/Scripts/User.js → Teknik/Areas/User/Scripts/SecuritySettings.js View File

@@ -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('<div class="alert alert-info alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>Recovery Email Verification Sent.</div>');
}
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('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + errorMsg + '</div>');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(html) + '</div>');
}
}
});
@@ -51,15 +44,8 @@
$("#authSetupStatus").html('<div class="alert alert-success alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>Success!</div>');
}
else {
errorMsg = html;
if (html.error) {
errorMsg = html.error;
if (html.error.message) {
errorMsg = html.error.message;
}
}
$("#authSetupStatus").css('display', 'inline', 'important');
$("#authSetupStatus").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + errorMsg + '</div>');
$("#authSetupStatus").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(html) + '</div>');
}
}
});
@@ -77,15 +63,8 @@
$("#top_msg").html('<div class="alert alert-success alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>Successfully Cleared Trusted Devices</div>');
}
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('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + errorMsg + '</div>');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(html) + '</div>');
}
}
});
@@ -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('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + errorMsg + '</div>');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(response) + '</div>');
}
}
});
@@ -147,43 +119,8 @@
$('#authTokenList').html('<li class="list-group-item text-center" id="noAuthTokens">No Authentication Tokens</li>');
}
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('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + errorMsg + '</div>');
}
}
});
}
});
});

$('#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('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + errorMsg + '</div>');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(response) + '</div>');
}
}
});
@@ -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('<div class="alert alert-success alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>Settings Saved!</div>');
}
}
else {
var error = html;
if (html.error)
error = html.error;
$("#top_msg").css('display', 'inline', 'important');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + error + '</div>');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(html) + '</div>');
}
}
});
@@ -268,11 +191,8 @@
$("#top_msg").html('<div class="alert alert-success alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>The Password Reset Link has been sent to your recovery email.</div>');
}
else {
var error = html;
if (html.error)
error = html.error;
$("#top_msg").css('display', 'inline', 'important');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + html.error + '</div>');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(html) + '</div>');
}
}
});
@@ -296,11 +216,8 @@
$("#top_msg").html('<div class="alert alert-success alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>Password has successfully been reset.</div>');
}
else {
var error = html;
if (html.error)
error = html.error;
$("#top_msg").css('display', 'inline', 'important');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + html.error + '</div>');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(html) + '</div>');
}
}
});
@@ -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('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + errorMsg + '</div>');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(response) + '</div>');
}
}
});
@@ -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('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + errorMsg + '</div>');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(response) + '</div>');
}
}
});
}
});
}
}

+ 22
- 0
Teknik/Areas/User/Scripts/Settings.js View File

@@ -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('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(html) + '</div>');
}
}
});
}
});
});
});

+ 29
- 0
Teknik/Areas/User/Scripts/UploadSettings.js View File

@@ -0,0 +1,29 @@
$(document).ready(function () {
$("[name='update_upload_encrypt']").bootstrapSwitch();

$("#update_submit").click(function () {
// Start Updating Animation
$.blockUI({ message: '<div class="text-center"><h3>Updating...</h3></div>' });
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('<div class="alert alert-success alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>Settings Saved!</div>');
}
else {
$("#top_msg").css('display', 'inline', 'important');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(html) + '</div>');
}
}
});
return false;
});
});

+ 77
- 5
Teknik/Areas/User/UserAreaRegistration.cs View File

@@ -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<string>() { "user" }, // Subdomains
new List<string>() { 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<string>() { "user" }, // Subdomains
new List<string>() { 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<string>() { "user" }, // Subdomains
new List<string>() { 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<string>() { "user" }, // Subdomains
new List<string>() { 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<string>() { "user" }, // Subdomains
new List<string>() { 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<string>() { "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"));
}
}
}
}

+ 33
- 0
Teknik/Areas/User/Utility/UserHelper.cs View File

@@ -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<AuthToken> 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();

+ 22
- 0
Teknik/Areas/User/ViewModels/BlogSettingsViewModel.cs View File

@@ -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;
}
}
}

+ 21
- 0
Teknik/Areas/User/ViewModels/InviteCodeViewModel.cs View File

@@ -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; }
}
}

+ 21
- 0
Teknik/Areas/User/ViewModels/InviteSettingsViewModel.cs View File

@@ -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<InviteCodeViewModel> AvailableCodes { get; set; }
public List<InviteCodeViewModel> ClaimedCodes { get; set; }

public InviteSettingsViewModel()
{
AvailableCodes = new List<InviteCodeViewModel>();
ClaimedCodes = new List<InviteCodeViewModel>();
}
}
}

+ 27
- 0
Teknik/Areas/User/ViewModels/ProfileSettingsViewModel.cs View File

@@ -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;
}
}
}

+ 5
- 2
Teknik/Areas/User/ViewModels/RegisterViewModel.cs View File

@@ -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; }
}
}
}

+ 50
- 0
Teknik/Areas/User/ViewModels/SecuritySettingsViewModel.cs View File

@@ -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<AuthTokenViewModel> AuthTokens { get; set; }

public SecuritySettingsViewModel()
{
TrustedDeviceCount = 0;
AuthTokens = new List<AuthTokenViewModel>();
RecoveryEmail = string.Empty;
RecoveryVerified = false;
AllowTrustedDevices = false;
TwoFactorEnabled = false;
TwoFactorKey = string.Empty;
PgpPublicKey = string.Empty;
}
}
}

+ 3
- 13
Teknik/Areas/User/ViewModels/SettingsViewModel.cs View File

@@ -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<AuthTokenViewModel> 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; }
}
}
}

+ 19
- 0
Teknik/Areas/User/ViewModels/UploadSettingsViewModel.cs View File

@@ -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;
}
}
}

+ 18
- 7
Teknik/Areas/User/Views/User/Register.cshtml View File

@@ -1,4 +1,4 @@
@model Teknik.Areas.Users.ViewModels.RegisterViewModel
@model Teknik.Areas.Users.ViewModels.RegisterViewModel

@using Teknik.Utilities

@@ -17,19 +17,30 @@
<form id="registrationForm" action="@Url.SubRouteUrl("user", "User.Register")" method="post" accept-charset="UTF-8">
<input name="Register.ReturnUrl" id="registerReturnUrl" type="hidden" value="@Model.ReturnUrl" />
<div class="form-group">
<input type="text" class="form-control" id="registerUsername" value="" placeholder="Username" name="Register.Username" data-val-required="The Username field is required." data-val="true"/>
<label for="registerUsername">Username</label>
<input type="text" class="form-control" id="registerUsername" value="" placeholder="Bob" name="Register.Username" data-val-required="The Username field is required." data-val="true"/>
</div>
<div class="form-group">
<input type="password" class="form-control" id="registerPassword" value="" placeholder="Password" name="Register.Password" data-val-required="The Password field is required." data-val="true"/>
<label for="registerPassword">Password</label>
<input type="password" class="form-control" id="registerPassword" value="" placeholder="********" name="Register.Password" data-val-required="The Password field is required." data-val="true"/>
</div>
<div class="form-group">
<input type="password" class="form-control" id="registerConfirmPassword" value="" placeholder="Confirm Password" name="Register.ConfirmPassword" data-val-required="The Confirm Password field is required." data-val="true" />
<label for="registerConfirmPassword">Confirm Password</label>
<input type="password" class="form-control" id="registerConfirmPassword" value="" placeholder="********" name="Register.ConfirmPassword" data-val-required="The Confirm Password field is required." data-val="true"/>
</div>
<div class="form-group">
<input type="text" class="form-control" id="registerRecoveryEmail" value="" placeholder="Recovery Email (Optional)" name="Register.RecoveryEmail" />
<label for="registerInviteCode">Invite Code@(Model.Config.UserConfig.InviteCodeRequired ? string.Empty : " (Optional)")</label>
<input type="text" class="form-control" id="registerInviteCode" value="@Model.InviteCode" placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" name="Register.InviteCode"/>
</div>
<div class="form-group">
<textarea class="form-control" id="registerPublicKey" name="Register.PublicKey" placeholder="PGP Public Key (Optional)" title="enter your pgp public key" rows="5"></textarea>
<label for="registerRecoveryEmail">Recovery Email (Optional)</label>
<input type="text" class="form-control" id="registerRecoveryEmail" value="" placeholder="user@example.com" name="Register.RecoveryEmail"/>
</div>
<div class="form-group">
<label for="registerPublicKey">PGP Public Key (Optional)</label>
<textarea class="form-control" id="registerPublicKey" name="Register.PublicKey" placeholder="-----BEGIN PGP PUBLIC KEY BLOCK-----
...
-----END PGP PUBLIC KEY BLOCK-----" title="enter your pgp public key" rows="5"></textarea>
</div>
<p class="text-center">
<small>
@@ -48,4 +59,4 @@
else
{
<h3>Registration has been disabled</h3>
}
}

+ 3
- 3
Teknik/Areas/User/Views/User/ResetPassword.cshtml View File

@@ -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")

<div class="container">
<div class="row">
@@ -31,4 +31,4 @@
</div>
</div>
</div>
</div>
</div>

+ 2
- 2
Teknik/Areas/User/Views/User/ResetPasswordVerification.cshtml View File

@@ -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")

<div class="container">
<div class="row">

+ 0
- 255
Teknik/Areas/User/Views/User/Settings.cshtml View File

@@ -1,255 +0,0 @@
@model Teknik.Areas.Users.ViewModels.SettingsViewModel

@using Teknik.Utilities
@using Teknik.Areas.Users.ViewModels

<script>
var homeUrl = '@Url.SubRouteUrl("www", "Home.Index")';
var editUserURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "Edit" })';
var deleteUserURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "Delete" })';
var resendVerifyURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "ResendVerifyRecoveryEmail"})';
var confirmAuthSetupURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "VerifyAuthenticatorCode" })';
var clearTrustedDevicesURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "ClearTrustedDevices" })';
var generateTokenURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "GenerateToken" })';
var revokeAllTokensURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "RevokeAllTokens" })';
var editTokenNameURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "EditTokenName" })';
var deleteTokenURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "DeleteToken" })';
</script>

@Styles.Render("~/Content/user")
@Scripts.Render("~/bundles/user")

<div class="container">
@if (!Model.Error)
{
<div class="modal fade" id="authenticatorSetup" tabindex="-1" role="dialog" aria-labelledby="authenticatorSetupLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="authSetupTitleLabel">Set Up a Third Party App to Generate Codes</h4>
</div>
<div class="modal-body">
<div class="row">
<div class="col-sm-12 text-center">
<div id="authSetupStatus"></div>
</div>
</div>
<form class="form" action="##" method="post" id="confirmAuthSetup">
<p>To get a third party app working, either scan the QR code below or type the secret key into the app.</p>
<div class="row">
<div class="col-sm-4">
<p class="text-muted">QR Code:</p>
</div>
<div class="col-sm-8">
<img id="authQRCode" src="@Url.SubRouteUrl("user", "User.Action", new { action = "GenerateAuthQrCode", key = Model.SecuritySettings.TwoFactorKey })" width="200" height="200" alt="qr code" />
</div>
</div>
<div class="row">
<div class="col-sm-4">
<p class="text-muted">Secret Key:</p>
</div>
<div class="col-sm-8">
<span class="text-success" id="authSetupSecretKey">@Model.SecuritySettings.TwoFactorKey</span>
</div>
</div>
<hr />
<p>To confirm the third party app is set up correctly, enter the security code that appears on your phone.</p>
<div class="row">
<div class="col-sm-4">
<p class="text-muted">Security Code:</p>
</div>
<div class="col-sm-6">
<input class="form-control" id="auth_setup_code" name="auth_setup_code" title="Authenticator Security Code" type="text" />
</div>
</div>
<hr />
<div class="form-group text-right">
<button class="btn btn-primary" id="auth_setup_verify" type="button" name="auth_setup_verify">Verify</button>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<form class="form" action="##" method="post" id="updateForm">
<input name="update_userid" id="update_userid" type="hidden" value="@Model.UserID" />
<!-- Tab Navigation -->
<ul class="nav nav-tabs" id="settingTabs">
<li class="active"><a href="#profile" data-toggle="tab"> Profile </a></li>
<li><a href="#security" data-toggle="tab"> Security </a></li>
<li><a href="#blog" data-toggle="tab"> Blog </a></li>
<li><a href="#uploads" data-toggle="tab"> Uploads </a></li>
</ul>
<div class="tab-content">
<!-- Profile Settings -->
<div class="tab-pane active" id="profile">
<div class="row">
<div class="form-group col-sm-4">
<label for="update_website"><h4>Website</h4></label>
<input class="form-control" id="update_website" name="update_website" placeholder="http://www.noneofyourbusiness.com/" title="enter your website" type="text" value="@Model.UserSettings.Website" />
</div>
<div class="form-group col-sm-8">
<label for="update_quote"><h4>Quote</h4></label>
<input class="form-control" id="update_quote" name="update_quote" placeholder="I have a dream!" title="enter a memorable quote" type="text" value="@Model.UserSettings.Quote" maxlength="140" />
</div>
</div>
<div class="row">
<div class="form-group col-sm-12">
<label for="update_about"><h4>About Yourself</h4></label>
<textarea class="form-control" name="update_about" id="update_about" placeholder="I'm awesome" title="enter any information you want to share with the world." data-provide="markdown" rows="10">@Model.UserSettings.About</textarea>
</div>
</div>
</div>
<!-- Security Settings -->
<div class="tab-pane" id="security">
<div class="row">
<div class="col-sm-4">
<div class="row">
<div class="form-group col-sm-12">
<label for="update_password_current"><h4>Current Password</h4></label>
<input class="form-control" name="update_password_current" id="update_password_current" placeholder="current password" title="enter your current password." type="password" />
</div>
</div>
<div class="row">
<div class="form-group col-sm-12">
<label for="update_password"><h4>New Password</h4></label>
<input class="form-control" name="update_password" id="update_password" placeholder="new password" title="enter your password." type="password" />
</div>
</div>
<div class="row">
<div class="form-group col-sm-12">
<label for="update_password_confirm"><h4>Verify New Password</h4></label>
<input class="form-control" name="update_password_confirm" id="update_password_confirm" placeholder="new password confirmed" title="enter your password again." type="password" />
</div>
</div>
</div>
<div class="col-sm-8">
<label for="update_pgp_public_key"><h4>Public Key</h4></label>
<textarea class="form-control" id="update_pgp_public_key" name="update_pgp_public_key" placeholder="Public Key Here" title="enter your pgp public key" rows="15">@Model.SecuritySettings.PGPSignature</textarea>
</div>
</div>
<div class="row">
<div class="col-sm-4 text-left">
<div class="row">
<div class="form-group col-sm-12">
<label for="update_recovery_email"><h4>Recovery Email</h4></label>
<input class="form-control" name="update_recovery_email" id="update_recovery_email" placeholder="user@example.com" title="enter a recovery email." type="text" value="@Model.SecuritySettings.RecoveryEmail" />
@if (!string.IsNullOrEmpty(Model.SecuritySettings.RecoveryEmail))
{
<p class="form-control-static">
@if (Model.SecuritySettings.RecoveryVerified)
{
<span class="text-success"><i class="fa fa-check"></i> Verified</span>
}
else
{
<span class="text-danger"><i class="fa fa-ban"></i> Unverified</span> <small><a href="#" class="text-primary" id="ResendVerification"><i class="fa fa-repeat"></i> Resend</a></small>
}
</p>
}
</div>
</div>
<div class="row">
<div class="form-group col-sm-12 text-left">
<label for="update_security_two_factor"><h4>Enable Two Factor Authentication</h4></label>
<div class="checkbox">
<label>
<input id="update_security_two_factor" name="update_security_two_factor" title="whether two factor authentication should occur for this account" type="checkbox" value="true" @(Model.SecuritySettings.TwoFactorEnabled ? "checked" : string.Empty) />
</label>
</div>
<p class="form-control-static @(Model.SecuritySettings.TwoFactorEnabled ? string.Empty : "hide")" id="setupAuthenticatorLink">
<small><a href="#" class="text-primary" id="SetupAuthenticator" data-toggle="modal" data-target="#authenticatorSetup"><i class="fa fa-lock"></i> Set Up Authenticator</a></small>
</p>
</div>
</div>
<div class="row">
<div class="form-group col-sm-12 text-left">
<label for="update_security_allow_trusted"><h4>Allow Trusted Devices</h4></label>
<div class="checkbox">
<label>
<input id="update_security_allow_trusted" name="update_security_allow_trusted" title="whether a device could be cached to bypass two factor authentication" type="checkbox" value="true" @(Model.SecuritySettings.AllowTrustedDevices ? "checked" : string.Empty) />
</label>
</div>
<p class="form-control-static @(Model.SecuritySettings.AllowTrustedDevices ? string.Empty : "hide")" id="ClearDevicesLink">
<small><a href="#" class="text-primary" id="ClearDevices">Clear Trusted Devices (@Model.TrustedDeviceCount)</a></small>
</p>
</div>
</div>
</div>
<div class="col-sm-8">
<br />
<label for="authTokens"><h4>Authentication Tokens</h4></label><span class="pull-right"><button type="button" class="btn btn-default" id="generate_token">Generate Token</button> <button type="button" class="btn btn-danger" id="revoke_all_tokens">Revoke All</button></span>
<div id="authTokens" style="overflow-y: auto; max-height: 400px;">
<ul class="list-group" id="authTokenList">
@if (Model.AuthTokens.Any())
{
foreach (AuthTokenViewModel token in Model.AuthTokens)
{
@Html.Partial("AuthToken", token)
}
}
else
{
<li class="list-group-item text-center" id="noAuthTokens">No Authentication Tokens</li>
}
</ul>
</div>
</div>
</div>
</div>
<!-- Blog Settings -->
<div class="tab-pane" id="blog">
<div class="row">
<div class="form-group col-sm-6">
<label for="update_blog_title"><h4>Title</h4></label>
<input class="form-control" id="update_blog_title" name="update_blog_title" placeholder="click bait" title="enter your blog's title" type="text" value="@Model.BlogSettings.Title" />
</div>
<div class="form-group col-sm-6">
<label for="update_blog_description"><h4>Description</h4></label>
<input class="form-control" id="update_blog_description" name="update_blog_description" placeholder="This blog is not worth reading." title="enter your blog's description" type="text" value="@Model.BlogSettings.Description" />
</div>
</div>
</div>
<!-- Upload Settings -->
<div class="tab-pane" id="uploads">
<div class="row">
<div class="col-sm-6">
<div class="checkbox">
<label>
<label for="update_upload_encrypt"><h4>Encrypt in Browser</h4></label>
<input id="update_upload_encrypt" name="update_upload_encrypt" title="whether the file should be encrypted in the browser before upload" type="checkbox" value="true" @(Model.UploadSettings.Encrypt ? "checked" : string.Empty) />
</label>
</div>
</div>
</div>
</div>
</div>
<!-- Save Settings -->
<div class="row">
<div class="form-group col-sm-10">
<br />
<button class="btn btn-lg btn-success" id="update_submit" type="submit"><i class="glyphicon glyphicon-ok-sign"></i> Save</button>
<button class="btn btn-lg" type="reset"><i class="glyphicon glyphicon-repeat"></i> Reset</button>
</div>
<div class="form-group col-sm-2">
<br />
<button type="button" class="btn btn-danger" id="delete_account">Delete Account</button>
</div>
</div>
</form>

</div><!--/col-9-->
</div><!--/row-->
}
else
{
<div class="row">
<div class="col-sm-12 text-center">
<h2>Sorry, but I couldn't find that user.</h2>
</div>
</div>
}
</div>

Teknik/Areas/User/Views/User/AuthToken.cshtml → Teknik/Areas/User/Views/User/Settings/AuthToken.cshtml View File


+ 36
- 0
Teknik/Areas/User/Views/User/Settings/BlogSettings.cshtml View File

@@ -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";
}

<script>
var editURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "EditBlog" })';
</script>

@Styles.Render("~/Content/user/settings/blog")
@Scripts.Render("~/bundles/user/settings/blog")


<form class="form" action="##" method="post" id="updateForm">
<div class="row">
<div class="form-group col-sm-6">
<label for="update_blog_title"><h4>Title</h4></label>
<input class="form-control" id="update_blog_title" name="update_blog_title" placeholder="click bait" title="enter your blog's title" type="text" value="@Model.Title" />
</div>
<div class="form-group col-sm-6">
<label for="update_blog_description"><h4>Description</h4></label>
<input class="form-control" id="update_blog_description" name="update_blog_description" placeholder="This blog is not worth reading." title="enter your blog's description" type="text" value="@Model.Description" />
</div>
</div>
<div class="row">
<div class="form-group col-sm-10">
<br />
<button class="btn btn-lg btn-success" id="update_submit" type="submit"><i class="glyphicon glyphicon-ok-sign"></i> Save</button>
<button class="btn btn-lg" type="reset"><i class="glyphicon glyphicon-repeat"></i> Reset</button>
</div>
</div>
</form>

+ 21
- 0
Teknik/Areas/User/Views/User/Settings/InviteCode.cshtml View File

@@ -0,0 +1,21 @@
@using Teknik.Utilities
@model Teknik.Areas.Users.ViewModels.InviteCodeViewModel

@{
bool codeClaimed = Model.ClaimedUser != null;
}

<li class="list-group-item @(Model.Active ? string.Empty : ".disabled")" id="inviteCode_@Model.InviteCodeId">
@if (!codeClaimed)
{
<div class="btn-group btn-group-sm pull-right" role="group" aria-label="...">
<button type="button" class="btn btn-default" id="copyCode_@Model.InviteCodeId" onclick="copyCode(@(Model.InviteCodeId), '@(Model.Code)');" data-toggle="popover" data-trigger="manual" data-placement="top" data-content="Copied to Clipboard" data-container="body"><i class="fa fa-clipboard"></i></button>
<button type="button" class="btn btn-default" onclick="createExternalLink(@Model.InviteCodeId);"><i class="fa fa-external-link"></i></button>
</div>
}
<h4 class="list-group-item-heading" id="inviteCode_Code_@Model.InviteCodeId">@Model.Code</h4>
@if (codeClaimed)
{
<p class="list-group-item-text">Claimed by <a href="@Url.SubRouteUrl("user", "User.ViewProfile", new {username = Model.ClaimedUser.Username})">@Model.ClaimedUser.Username</a></p>
}
</li>

+ 59
- 0
Teknik/Areas/User/Views/User/Settings/InviteSettings.cshtml View File

@@ -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")

<script>
var createExternalLinkURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "CreateInviteCodeLink" })';
</script>

<div class="row">
<div class="col-sm-10 col-sm-offset-1">
<br />
<label for="availableCodes"><h4>Available Invite Codes</h4></label>
<div id="availableCodes" style="overflow-y: auto; max-height: 400px;">
<ul class="list-group" id="availableCodeList">
@if (Model.AvailableCodes.Any())
{
foreach (InviteCodeViewModel code in Model.AvailableCodes)
{
@Html.Partial("Settings/InviteCode", code)
}
}
else
{
<li class="list-group-item text-center" id="noAvailableCodes">No Invite Codes Available</li>
}
</ul>
</div>
</div>
</div>

<div class="row">
<div class="col-sm-10 col-sm-offset-1">
<br />
<label for="claimedCodes"><h4>Claimed Invite Codes</h4></label>
<div id="claimedCodes" style="overflow-y: auto; max-height: 400px;">
<ul class="list-group" id="claimedCodeList">
@if (Model.ClaimedCodes.Any())
{
foreach (InviteCodeViewModel code in Model.ClaimedCodes)
{
@Html.Partial("Settings/InviteCode", code)
}
}
else
{
<li class="list-group-item text-center" id="noClaimedCodes">No Invite Codes have been Claimed</li>
}
</ul>
</div>
</div>
</div>

+ 41
- 0
Teknik/Areas/User/Views/User/Settings/ProfileSettings.cshtml View File

@@ -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";
}

<script>
var editURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "EditProfile" })';
</script>

@Styles.Render("~/Content/user/settings/profile")
@Scripts.Render("~/bundles/user/settings/profile")

<form class="form" action="##" method="post" id="updateForm">
<div class="row">
<div class="form-group col-sm-4">
<label for="update_website"><h4>Website</h4></label>
<input class="form-control" id="update_website" name="update_website" placeholder="http://www.noneofyourbusiness.com/" title="enter your website" type="text" value="@Model.Website" />
</div>
<div class="form-group col-sm-8">
<label for="update_quote"><h4>Quote</h4></label>
<input class="form-control" id="update_quote" name="update_quote" placeholder="I have a dream!" title="enter a memorable quote" type="text" value="@Model.Quote" maxlength="140" />
</div>
</div>
<div class="row">
<div class="form-group col-sm-12">
<label for="update_about"><h4>About Yourself</h4></label>
<textarea class="form-control" name="update_about" id="update_about" placeholder="I'm awesome" title="enter any information you want to share with the world." data-provide="markdown" rows="10">@Model.About</textarea>
</div>
</div>
<div class="row">
<div class="form-group col-sm-10">
<br />
<button class="btn btn-lg btn-success" id="update_submit" type="submit"><i class="glyphicon glyphicon-ok-sign"></i> Save</button>
<button class="btn btn-lg" type="reset"><i class="glyphicon glyphicon-repeat"></i> Reset</button>
</div>
</div>
</form>

+ 178
- 0
Teknik/Areas/User/Views/User/Settings/SecuritySettings.cshtml View File

@@ -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";
}

<script>
var homeUrl = '@Url.SubRouteUrl("www", "Home.Index")';
var editURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "EditSecurity" })';
var resendVerifyURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "ResendVerifyRecoveryEmail"})';
var confirmAuthSetupURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "VerifyAuthenticatorCode" })';
var clearTrustedDevicesURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "ClearTrustedDevices" })';
var generateTokenURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "GenerateToken" })';
var revokeAllTokensURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "RevokeAllTokens" })';
var editTokenNameURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "EditTokenName" })';
var deleteTokenURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "DeleteToken" })';
</script>

@Styles.Render("~/Content/user/settings/security")
@Scripts.Render("~/bundles/user/settings/security")

<div class="modal fade" id="authenticatorSetup" tabindex="-1" role="dialog" aria-labelledby="authenticatorSetupLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="authSetupTitleLabel">Set Up a Third Party App to Generate Codes</h4>
</div>
<div class="modal-body">
<div class="row">
<div class="col-sm-12 text-center">
<div id="authSetupStatus"></div>
</div>
</div>
<form class="form" action="##" method="post" id="confirmAuthSetup">
<p>To get a third party app working, either scan the QR code below or type the secret key into the app.</p>
<div class="row">
<div class="col-sm-4">
<p class="text-muted">QR Code:</p>
</div>
<div class="col-sm-8">
<img id="authQRCode" src="@Url.SubRouteUrl("user", "User.Action", new { action = "GenerateAuthQrCode", key = Model.TwoFactorKey })" width="200" height="200" alt="qr code" />
</div>
</div>
<div class="row">
<div class="col-sm-4">
<p class="text-muted">Secret Key:</p>
</div>
<div class="col-sm-8">
<span class="text-success" id="authSetupSecretKey">@Model.TwoFactorKey</span>
</div>
</div>
<hr />
<p>To confirm the third party app is set up correctly, enter the security code that appears on your phone.</p>
<div class="row">
<div class="col-sm-4">
<p class="text-muted">Security Code:</p>
</div>
<div class="col-sm-6">
<input class="form-control" id="auth_setup_code" name="auth_setup_code" title="Authenticator Security Code" type="text" />
</div>
</div>
<hr />
<div class="form-group text-right">
<button class="btn btn-primary" id="auth_setup_verify" type="button" name="auth_setup_verify">Verify</button>
</div>
</form>
</div>
</div>
</div>
</div>

<form class="form" action="##" method="post" id="updateForm">
<div class="row">
<div class="col-sm-4">
<div class="row">
<div class="form-group col-sm-12">
<label for="update_password_current"><h4>Current Password</h4></label>
<input class="form-control" name="update_password_current" id="update_password_current" placeholder="current password" title="enter your current password." type="password" />
</div>
</div>
<div class="row">
<div class="form-group col-sm-12">
<label for="update_password"><h4>New Password</h4></label>
<input class="form-control" name="update_password" id="update_password" placeholder="new password" title="enter your password." type="password" />
</div>
</div>
<div class="row">
<div class="form-group col-sm-12">
<label for="update_password_confirm"><h4>Verify New Password</h4></label>
<input class="form-control" name="update_password_confirm" id="update_password_confirm" placeholder="new password confirmed" title="enter your password again." type="password" />
</div>
</div>