1
0
mirror of https://git.teknik.io/Teknikode/Teknik.git synced 2023-08-02 14:16:22 +02:00

- Implemented invite codes to use for registration.

- Reorganized settings pages into individual pages.
This commit is contained in:
Uncled1023 2018-01-16 22:36:11 -08:00
parent 61e0204361
commit a7c314d728
41 changed files with 1422 additions and 458 deletions

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

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>');
}
}
});
});
});

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>

View File

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

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

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>();
}
}
}

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

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>');
}
}
});
}

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

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

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>');
}
}
});
}
});
}
}

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>');
}
}
});
}
});
});
});

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

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

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

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

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

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>();
}
}
}

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

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

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

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

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

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

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>

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

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>

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>

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>

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>

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>

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>
</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.PgpPublicKey</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.RecoveryEmail" />
@if (!string.IsNullOrEmpty(Model.RecoveryEmail))
{
<p class="form-control-static">
@if (Model.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.TwoFactorEnabled ? "checked" : string.Empty) />
</label>
</div>
<p class="form-control-static @(Model.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.AllowTrustedDevices ? "checked" : string.Empty) />
</label>
</div>
<p class="form-control-static @(Model.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("Settings/AuthToken", token)
}
}
else
{
<li class="list-group-item text-center" id="noAuthTokens">No Authentication Tokens</li>
}
</ul>
</div>
</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>

View File

@ -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")
<script>
var homeUrl = '@Url.SubRouteUrl("www", "Home.Index")';
var deleteUserURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "Delete" })';
</script>
<div class="container">
@if (!Model.Error)
{
<div class="row">
<div class="col-sm-2">
<!--left col-->
<div class="panel panel-default">
<ul class="list-group">
<div class="panel-heading text-center"><strong>Account Settings</strong></div>
<a href="@Url.SubRouteUrl("user", "User.SecuritySettings")" class="list-group-item @(Model.Page == "Security" ? "active" : string.Empty)">Security</a>
<a href="@Url.SubRouteUrl("user", "User.ProfileSettings")" class="list-group-item @(Model.Page == "Profile" ? "active" : string.Empty)">Profile</a>
<a href="@Url.SubRouteUrl("user", "User.InviteSettings")" class="list-group-item @(Model.Page == "Invite" ? "active" : string.Empty)">Invite Codes (@(User.Info.OwnedInviteCodes.Where(c => c.Active).Count()))</a>
<a href="@Url.SubRouteUrl("user", "User.BlogSettings")" class="list-group-item @(Model.Page == "Blog" ? "active" : string.Empty)">Blogging</a>
<a href="@Url.SubRouteUrl("user", "User.UploadSettings")" class="list-group-item @(Model.Page == "Upload" ? "active" : string.Empty)">Uploads</a>
</ul>
</div>
<div class="list-group text-center">
<button type="button" class="list-group-item btn-danger" id="delete_account">Delete Account</button>
</div>
</div><!--/col-2-->
<div class="col-sm-10">
@RenderBody()
</div><!--/col-10-->
</div><!--/row-->
}
else
{
<div class="row">
<div class="col-sm-12 text-center">
<h2>@Model.ErrorMessage</h2>
</div>
</div>
}
</div>

View File

@ -0,0 +1,12 @@
@model Teknik.Areas.Users.ViewModels.SettingsViewModel
@{
Layout = "~/Areas/User/Views/User/Settings/Settings.cshtml";
}
<div class="row">
<div class="col-sm-10 col-sm-offset-1">
<p>Default Account Settings</p>
</div>
</div>

View File

@ -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";
}
<script>
var homeUrl = '@Url.SubRouteUrl("www", "Home.Index")';
var editURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "EditUpload" })';
</script>
@Styles.Render("~/Content/user/settings/upload")
@Scripts.Render("~/bundles/user/settings/upload")
<form class="form" action="##" method="post" id="updateForm">
<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.Encrypt ? "checked" : string.Empty) />
</label>
</div>
</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>

View File

@ -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)
{
<div class="nav navbar-nav pull-right">
<div class="nav navbar-nav pull-right">
@if (Request.IsAuthenticated)
{
<li class="dropdown">
@ -22,9 +22,9 @@
</li>
@if (User.IsInRole("Admin"))
{
<li>
<a href="@Url.SubRouteUrl("admin", "Admin.Dashboard")">Administration</a>
</li>
<li>
<a href="@Url.SubRouteUrl("admin", "Admin.Dashboard")">Administration</a>
</li>
}
<li>
<a href="@Url.SubRouteUrl("user", "User.Logout")">Sign Out</a>
@ -49,5 +49,5 @@
</noscript>
}
}
</div>
}
</div>
}

View File

@ -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<TrustedDevice> TrustedDevices { get; set; }
public DbSet<AuthToken> AuthTokens { get; set; }
public DbSet<TransferType> TransferTypes { get; set; }
public DbSet<InviteCode> InviteCodes { get; set; }
// User Settings
public DbSet<UserSettings> UserSettings { get; set; }
public DbSet<SecuritySettings> SecuritySettings { get; set; }
@ -115,6 +116,16 @@ namespace Teknik.Models
.WithOptional(u => u.User)
.WillCascadeOnDelete(false);
modelBuilder.Entity<User>()
.HasMany(u => u.OwnedInviteCodes)
.WithOptional(c => c.Owner)
.WillCascadeOnDelete(false);
modelBuilder.Entity<User>()
.HasOptional(u => u.ClaimedInviteCode)
.WithOptionalPrincipal(c => c.ClaimedUser)
.WillCascadeOnDelete(false);
// Upload Mappings
modelBuilder.Entity<Upload>()
.HasOptional(u => u.User);
@ -130,13 +141,14 @@ namespace Teknik.Models
// Vault Mappings
modelBuilder.Entity<Vault>()
.HasOptional(u => u.User);
// Users
modelBuilder.Entity<User>().ToTable("Users");
modelBuilder.Entity<Group>().ToTable("Groups");
modelBuilder.Entity<Role>().ToTable("Roles");
modelBuilder.Entity<TrustedDevice>().ToTable("TrustedDevices");
modelBuilder.Entity<AuthToken>().ToTable("AuthTokens");
modelBuilder.Entity<InviteCode>().ToTable("InviteCodes");
modelBuilder.Entity<TransferType>().ToTable("TransferTypes");
modelBuilder.Entity<RecoveryEmailVerification>().ToTable("RecoveryEmailVerifications");
modelBuilder.Entity<ResetPasswordVerification>().ToTable("ResetPasswordVerifications");
@ -176,4 +188,4 @@ namespace Teknik.Models
base.OnModelCreating(modelBuilder);
}
}
}
}

View File

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

View File

@ -293,15 +293,22 @@
<Compile Include="Areas\Status\ViewModels\TransactionViewModel.cs" />
<Compile Include="Areas\User\Controllers\UserController.cs" />
<Compile Include="Areas\User\Models\BlogSettings.cs" />
<Compile Include="Areas\User\Models\InviteCode.cs" />
<Compile Include="Areas\User\Models\ResetPasswordVerification.cs" />
<Compile Include="Areas\User\Models\RecoveryEmailVerification.cs" />
<Compile Include="Areas\User\Models\SecuritySettings.cs" />
<Compile Include="Areas\User\Models\AuthToken.cs" />
<Compile Include="Areas\User\Models\TrustedDevice.cs" />
<Compile Include="Areas\User\ViewModels\AuthTokenViewModel.cs" />
<Compile Include="Areas\User\ViewModels\InviteCodeViewModel.cs" />
<Compile Include="Areas\User\ViewModels\InviteSettingsViewModel.cs" />
<Compile Include="Areas\User\ViewModels\UploadSettingsViewModel.cs" />
<Compile Include="Areas\User\ViewModels\EditSettingsViewModel.cs" />
<Compile Include="Areas\User\ViewModels\GetPremiumViewModel.cs" />
<Compile Include="Areas\User\ViewModels\BlogSettingsViewModel.cs" />
<Compile Include="Areas\User\ViewModels\SecuritySettingsViewModel.cs" />
<Compile Include="Areas\User\ViewModels\SetPasswordViewModel.cs" />
<Compile Include="Areas\User\ViewModels\ProfileSettingsViewModel.cs" />
<Compile Include="Areas\User\ViewModels\TwoFactorViewModel.cs" />
<Compile Include="Areas\Vault\ViewModels\ModifyVaultViewModel.cs" />
<Compile Include="Areas\Vault\ViewModels\ModifyVaultItemViewModel.cs" />
@ -410,7 +417,13 @@
<Content Include="Areas\Upload\Content\Upload.css" />
<Content Include="Areas\User\Scripts\CheckAuthCode.js" />
<Content Include="Areas\User\Scripts\Profile.js" />
<Content Include="Areas\User\Scripts\User.js" />
<Content Include="Areas\User\Scripts\Settings.js" />
<Content Include="Areas\User\Scripts\BlogSettings.js" />
<Content Include="Areas\User\Scripts\InviteSettings.js" />
<Content Include="Areas\User\Scripts\ProfileSettings.js" />
<Content Include="Areas\User\Scripts\SecuritySettings.js" />
<Content Include="Areas\User\Scripts\UploadSettings.js" />
<Content Include="Areas\User\Scripts\ResetPass.js" />
<Content Include="Areas\Shortener\Scripts\Shortener.js" />
<Content Include="Areas\Upload\Scripts\Download.js" />
<Content Include="Areas\Vault\Content\Vault.css" />
@ -598,7 +611,7 @@
<Content Include="Areas\Podcast\Views\Podcast\Comments.cshtml" />
<Content Include="Areas\Podcast\Views\Podcast\ViewPodcast.cshtml" />
<Content Include="App_Data\Config.json" />
<Content Include="Areas\User\Views\User\Settings.cshtml" />
<Content Include="Areas\User\Views\User\Settings\Settings.cshtml" />
<Content Include="Areas\Vault\Views\_ViewStart.cshtml" />
<Content Include="Areas\Help\Views\Help\RSS.cshtml" />
<Content Include="Areas\Stream\Views\web.config" />
@ -643,7 +656,7 @@
<Content Include="Fonts\fontawesome-webfont.woff" />
<Content Include="Fonts\fontawesome-webfont.ttf" />
<Content Include="Fonts\fontawesome-webfont.eot" />
<Content Include="Areas\User\Views\User\AuthToken.cshtml" />
<Content Include="Areas\User\Views\User\Settings\AuthToken.cshtml" />
<Content Include="Areas\Error\Views\Error\Http401.cshtml" />
<Content Include="Areas\Vault\Views\Vault\ModifyVault.cshtml" />
<Content Include="Areas\Vault\Views\Vault\ModifyVaultItem.cshtml" />
@ -666,6 +679,13 @@
<Content Include="Areas\IRC\Views\web.config" />
<Content Include="Areas\IRC\Views\IRC\Client.cshtml" />
<Content Include="Areas\IRC\Views\_ViewStart.cshtml" />
<Content Include="Areas\User\Views\User\Settings\ProfileSettings.cshtml" />
<Content Include="Areas\User\Views\User\Settings\SecuritySettings.cshtml" />
<Content Include="Areas\User\Views\User\Settings\InviteSettings.cshtml" />
<Content Include="Areas\User\Views\User\Settings\BlogSettings.cshtml" />
<Content Include="Areas\User\Views\User\Settings\UploadSettings.cshtml" />
<Content Include="Areas\User\Views\User\Settings\SettingsBase.cshtml" />
<Content Include="Areas\User\Views\User\Settings\InviteCode.cshtml" />
<None Include="Properties\PublishProfiles\Teknik Dev.pubxml" />
<None Include="Properties\PublishProfiles\Teknik Production.pubxml" />
<None Include="Scripts\jquery-2.1.4.intellisense.js" />

View File

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