mirror of
https://github.com/immense/Remotely.git
synced 2025-10-26 11:27:15 +00:00
Change organization management to use Razor Pages model binding. New accounts are created in current organization.
This commit is contained in:
parent
62245979b7
commit
4a345bff2c
@ -73,22 +73,24 @@ namespace Remotely.Server.API
|
||||
return Ok("ok");
|
||||
}
|
||||
|
||||
[HttpPut("Name")]
|
||||
[HttpDelete("DeleteUser/{userID}")]
|
||||
[ServiceFilter(typeof(ApiAuthorizationFilter))]
|
||||
public IActionResult Name([FromBody]string organizationName)
|
||||
public async Task<IActionResult> DeleteUser(string userID)
|
||||
{
|
||||
if (User.Identity.IsAuthenticated &&
|
||||
!DataService.GetUserByName(User.Identity.Name).IsAdministrator)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
if (organizationName.Length > 25)
|
||||
|
||||
if (User.Identity.IsAuthenticated &&
|
||||
DataService.GetUserByName(User.Identity.Name).Id == userID)
|
||||
{
|
||||
return BadRequest();
|
||||
return BadRequest("You can't delete yourself here. You must go to the Personal Data page to delete your own account.");
|
||||
}
|
||||
|
||||
Request.Headers.TryGetValue("OrganizationID", out var orgID);
|
||||
DataService.UpdateOrganizationName(orgID, organizationName.Trim());
|
||||
await DataService.RemoveUserFromOrganization(orgID, userID);
|
||||
return Ok("ok");
|
||||
}
|
||||
|
||||
@ -131,27 +133,55 @@ namespace Remotely.Server.API
|
||||
return Ok(deviceGroupID);
|
||||
}
|
||||
|
||||
[HttpDelete("DeleteUser/{userID}")]
|
||||
[HttpGet("GenerateResetUrl")]
|
||||
[ServiceFilter(typeof(ApiAuthorizationFilter))]
|
||||
public async Task<IActionResult> DeleteUser(string userID)
|
||||
public async Task<IActionResult> GenerateResetUrl(string userEmail)
|
||||
{
|
||||
if (User.Identity.IsAuthenticated &&
|
||||
!DataService.GetUserByName(User.Identity.Name).IsAdministrator)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
Request.Headers.TryGetValue("OrganizationID", out var orgID);
|
||||
|
||||
var user = await UserManager.FindByEmailAsync(userEmail);
|
||||
|
||||
if (user.OrganizationID != orgID)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
var code = await UserManager.GeneratePasswordResetTokenAsync(user);
|
||||
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
|
||||
var callbackUrl = Url.Page(
|
||||
"/Account/ResetPassword",
|
||||
pageHandler: null,
|
||||
values: new { area = "Identity", code },
|
||||
protocol: Request.Scheme);
|
||||
|
||||
return Ok(callbackUrl);
|
||||
|
||||
}
|
||||
|
||||
[HttpPut("Name")]
|
||||
[ServiceFilter(typeof(ApiAuthorizationFilter))]
|
||||
public IActionResult Name([FromBody]string organizationName)
|
||||
{
|
||||
if (User.Identity.IsAuthenticated &&
|
||||
!DataService.GetUserByName(User.Identity.Name).IsAdministrator)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
if (User.Identity.IsAuthenticated &&
|
||||
DataService.GetUserByName(User.Identity.Name).Id == userID)
|
||||
if (organizationName.Length > 25)
|
||||
{
|
||||
return BadRequest("You can't delete yourself here. You must go to the Personal Data page to delete your own account.");
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
Request.Headers.TryGetValue("OrganizationID", out var orgID);
|
||||
await DataService.RemoveUserFromOrganization(orgID, userID);
|
||||
DataService.UpdateOrganizationName(orgID, organizationName.Trim());
|
||||
return Ok("ok");
|
||||
}
|
||||
|
||||
[HttpPost("SendInvite")]
|
||||
[ServiceFilter(typeof(ApiAuthorizationFilter))]
|
||||
public async Task<IActionResult> SendInvite([FromBody]Invite invite)
|
||||
@ -166,54 +196,49 @@ namespace Remotely.Server.API
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
var newUserMessage = "";
|
||||
Request.Headers.TryGetValue("OrganizationID", out var orgID);
|
||||
|
||||
|
||||
if (!DataService.DoesUserExist(invite.InvitedUser))
|
||||
{
|
||||
var user = new RemotelyUser { UserName = invite.InvitedUser, Email = invite.InvitedUser };
|
||||
{
|
||||
var user = new RemotelyUser { UserName = invite.InvitedUser, Email = invite.InvitedUser, OrganizationID = orgID };
|
||||
var result = await UserManager.CreateAsync(user);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
if (!DataService.SetNewUserProperties(user.UserName, orgID, invite.IsAdmin))
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
|
||||
user = await UserManager.FindByEmailAsync(invite.InvitedUser);
|
||||
|
||||
await UserManager.ConfirmEmailAsync(user, await UserManager.GenerateEmailConfirmationTokenAsync(user));
|
||||
|
||||
var code = await UserManager.GeneratePasswordResetTokenAsync(user);
|
||||
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
|
||||
var callbackUrl = Url.Page(
|
||||
"/Account/ResetPassword",
|
||||
pageHandler: null,
|
||||
values: new { area = "Identity", code },
|
||||
protocol: Request.Scheme);
|
||||
|
||||
invite.ResetUrl = callbackUrl;
|
||||
|
||||
newUserMessage = $@"<br><br>Since you don't have an account yet, one has been created for you.
|
||||
You will need to set a password first before attempting to join the organization.<br><br>
|
||||
Set your password by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>. Your username/email
|
||||
is <strong>{invite.InvitedUser}</strong>.";
|
||||
return Ok();
|
||||
}
|
||||
else
|
||||
{
|
||||
return BadRequest("There was an issue creating the new account.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var newInvite = DataService.AddInvite(orgID, invite);
|
||||
|
||||
Request.Headers.TryGetValue("OrganizationID", out var orgID);
|
||||
|
||||
var newInvite = DataService.AddInvite(orgID, invite);
|
||||
|
||||
var inviteURL = $"{Request.Scheme}://{Request.Host}/Invite?id={newInvite.ID}";
|
||||
await EmailSender.SendEmailAsync(invite.InvitedUser, "Invitation to Organization in Remotely",
|
||||
$@"<img src='https://remotely.lucency.co/images/Remotely_Logo.png'/>
|
||||
var inviteURL = $"{Request.Scheme}://{Request.Host}/Invite?id={newInvite.ID}";
|
||||
await EmailSender.SendEmailAsync(invite.InvitedUser, "Invitation to Organization in Remotely",
|
||||
$@"<img src='https://remotely.lucency.co/images/Remotely_Logo.png'/>
|
||||
<br><br>
|
||||
Hello!
|
||||
<br><br>
|
||||
You've been invited to join an organization in Remotely.
|
||||
{newUserMessage}
|
||||
<br><br>
|
||||
You can join the organization by <a href='{HtmlEncoder.Default.Encode(inviteURL)}'>clicking here</a>.");
|
||||
|
||||
return Ok(newInvite);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,13 +7,7 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@if (!string.IsNullOrWhiteSpace(Model.Message))
|
||||
{
|
||||
<div class="alert alert-success alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
@Model.Message
|
||||
</div>
|
||||
}
|
||||
<partial name="_StatusMessage" for="Message" />
|
||||
<form method="post">
|
||||
<div asp-validation-summary="All"></div>
|
||||
|
||||
|
||||
@ -12,6 +12,10 @@
|
||||
|
||||
@if (isAdmin)
|
||||
{
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
@* Organization ID *@
|
||||
@ -73,25 +77,28 @@
|
||||
<tr>
|
||||
<th>User Name</th>
|
||||
<th>Administrator</th>
|
||||
<th>Reset Password</th>
|
||||
<th>Remove</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (var i = 0; i < Model.Users.Count; i++)
|
||||
{
|
||||
<tr user="@Model.Users[i].ID">
|
||||
<td class="middle-aligned"><label class="control-label">@Model.Users[i].UserName</label></td>
|
||||
@if (currentUser.Id == Model.Users[i].ID)
|
||||
{
|
||||
<td>@Html.CheckBoxFor(x => x.Users[i].IsAdmin, new { user = Model.Users[i].ID, @class = "user-is-admin-checkbox", disabled = "disabled" })</td>
|
||||
<td><button type="button" class="btn btn-danger delete-user-button" user="@Model.Users[i].ID" disabled>Delete</button></td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td>@Html.CheckBoxFor(x => x.Users[i].IsAdmin, new { user = Model.Users[i].ID, @class = "user-is-admin-checkbox" })</td>
|
||||
<td><button type="button" class="btn btn-danger delete-user-button" user="@Model.Users[i].ID">Delete</button></td>
|
||||
}
|
||||
</tr>
|
||||
<tr user="@Model.Users[i].ID">
|
||||
<td class="middle-aligned"><label class="control-label">@Model.Users[i].UserName</label></td>
|
||||
@if (currentUser.Id == Model.Users[i].ID)
|
||||
{
|
||||
<td>@Html.CheckBoxFor(x => x.Users[i].IsAdmin, new { user = Model.Users[i].ID, @class = "user-is-admin-checkbox", disabled = "disabled" })</td>
|
||||
<td></td>
|
||||
<td><button type="button" class="btn btn-danger delete-user-button" user="@Model.Users[i].ID" disabled>Delete</button></td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td>@Html.CheckBoxFor(x => x.Users[i].IsAdmin, new { user = Model.Users[i].ID, @class = "user-is-admin-checkbox" })</td>
|
||||
<td><button type="button" class="btn btn-danger reset-password-button" user="@Model.Users[i].ID">Reset</button></td>
|
||||
<td><button type="button" class="btn btn-danger delete-user-button" user="@Model.Users[i].ID">Delete</button></td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
@ -120,15 +127,10 @@
|
||||
{
|
||||
<tr invite="@Model.Invites[i].ID">
|
||||
<td class="middle-aligned"><label class="control-label">@Model.Invites[i].InvitedUser</label></td>
|
||||
<td class="middle-aligned text-center">@Html.CheckBoxFor(x => x.Invites[i].IsAdmin, new { disabled = "disabled" })</td>
|
||||
<td class="middle-aligned">@Html.CheckBoxFor(x => x.Invites[i].IsAdmin, new { disabled = "disabled" })</td>
|
||||
<td class="middle-aligned">
|
||||
<label class="control-label">
|
||||
<a href="@Request.Scheme://@Request.Host/Invite/?id=@Model.Invites[i].ID">Join Link</a>
|
||||
@if (!string.IsNullOrWhiteSpace(Model.Invites[i].ResetUrl))
|
||||
{
|
||||
<br />
|
||||
<a href="@Model.Invites[i].ResetUrl">Password Reset</a>
|
||||
}
|
||||
</label>
|
||||
</td>
|
||||
<td><button type="button" class="btn btn-danger delete-invite-button" invite="@Model.Invites[i].ID">Delete</button></td>
|
||||
@ -144,22 +146,26 @@
|
||||
@* Send Invites *@
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<input id="inviteUserInput" placeholder="Username/email to invite" type="email" required class="form-control" />
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text pr-1">Admin?</span>
|
||||
</div>
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text pl-1">
|
||||
<input id="inviteIsAdmin" class="checkbox-inline" type="checkbox" />
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<form method="post" asp-page-handler="SendInvite">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<input asp-for="Input.UserEmail" placeholder="Username/email to invite" type="email" required class="form-control" />
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text pr-1">Admin?</span>
|
||||
</div>
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text pl-1">
|
||||
<input asp-for="Input.IsAdmin" class="checkbox-inline" type="checkbox" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group-append">
|
||||
<button type="submit" class="btn btn-secondary">Invite</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group-append">
|
||||
<button id="sendInviteButton" type="button" class="btn btn-secondary">Invite</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -8,14 +8,21 @@ using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Remotely.Shared.ViewModels.Organization;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Threading.Tasks;
|
||||
using System.Text.Encodings.Web;
|
||||
using Microsoft.AspNetCore.Identity.UI.Services;
|
||||
using System;
|
||||
|
||||
namespace Remotely.Server.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class OrganizationModel : PageModel
|
||||
{
|
||||
public OrganizationModel(DataService dataService)
|
||||
public OrganizationModel(DataService dataService, UserManager<RemotelyUser> userManager, IEmailSender emailSender)
|
||||
{
|
||||
DataService = dataService;
|
||||
UserManager = userManager;
|
||||
EmailSender = emailSender;
|
||||
}
|
||||
public List<SelectListItem> DeviceGroups { get; } = new List<SelectListItem>();
|
||||
|
||||
@ -29,8 +36,31 @@ namespace Remotely.Server.Areas.Identity.Pages.Account.Manage
|
||||
[Display(Name = "Users")]
|
||||
public List<OrganizationUser> Users { get; set; }
|
||||
|
||||
public class InputModel
|
||||
{
|
||||
public bool IsAdmin { get; set; }
|
||||
|
||||
[EmailAddress]
|
||||
public string UserEmail { get; set; }
|
||||
}
|
||||
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; } = new InputModel();
|
||||
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
|
||||
private DataService DataService { get; }
|
||||
|
||||
private UserManager<RemotelyUser> UserManager { get; }
|
||||
private IEmailSender EmailSender { get; }
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
PopulateViewModel();
|
||||
}
|
||||
|
||||
private void PopulateViewModel()
|
||||
{
|
||||
OrganizationName = DataService.GetOrganizationName(User.Identity.Name);
|
||||
|
||||
@ -50,10 +80,73 @@ namespace Remotely.Server.Areas.Identity.Pages.Account.Manage
|
||||
ID = x.ID,
|
||||
InvitedUser = x.InvitedUser,
|
||||
IsAdmin = x.IsAdmin,
|
||||
DateSent = x.DateSent,
|
||||
ResetUrl = x.ResetUrl
|
||||
DateSent = x.DateSent
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostSendInviteAsync()
|
||||
{
|
||||
var currentUser = await UserManager.FindByEmailAsync(User.Identity.Name);
|
||||
if (!currentUser.IsAdministrator)
|
||||
{
|
||||
return RedirectToPage("Index");
|
||||
}
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
if (!DataService.DoesUserExist(Input.UserEmail))
|
||||
{
|
||||
var user = new RemotelyUser { UserName = Input.UserEmail, Email = Input.UserEmail };
|
||||
var result = await UserManager.CreateAsync(user);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
if (!DataService.SetNewUserProperties(user.UserName, currentUser.OrganizationID, Input.IsAdmin))
|
||||
{
|
||||
ModelState.AddModelError("OrgID", "Failed to set organization ID.");
|
||||
return Page();
|
||||
}
|
||||
|
||||
user = await UserManager.FindByEmailAsync(Input.UserEmail);
|
||||
|
||||
|
||||
await UserManager.ConfirmEmailAsync(user, await UserManager.GenerateEmailConfirmationTokenAsync(user));
|
||||
|
||||
StatusMessage = "User account created.";
|
||||
return RedirectToPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
ModelState.AddModelError("CreateUser", "Failed to create user account.");
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
var invite = new Invite()
|
||||
{
|
||||
InvitedUser = Input.UserEmail,
|
||||
IsAdmin = Input.IsAdmin
|
||||
};
|
||||
var newInvite = DataService.AddInvite(currentUser.OrganizationID, invite);
|
||||
|
||||
var inviteURL = $"{Request.Scheme}://{Request.Host}/Invite?id={newInvite.ID}";
|
||||
await EmailSender.SendEmailAsync(invite.InvitedUser, "Invitation to Organization in Remotely",
|
||||
$@"<img src='https://remotely.lucency.co/images/Remotely_Logo.png'/>
|
||||
<br><br>
|
||||
Hello!
|
||||
<br><br>
|
||||
You've been invited to join an organization in Remotely.
|
||||
<br><br>
|
||||
You can join the organization by <a href='{HtmlEncoder.Default.Encode(inviteURL)}'>clicking here</a>.");
|
||||
|
||||
StatusMessage = "Invitation sent.";
|
||||
|
||||
return RedirectToPage();
|
||||
}
|
||||
}
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -82,8 +82,7 @@ namespace Remotely.Server.Services
|
||||
InvitedUser = invite.InvitedUser,
|
||||
IsAdmin = invite.IsAdmin,
|
||||
Organization = organization,
|
||||
OrganizationID = organization.ID,
|
||||
ResetUrl = invite.ResetUrl
|
||||
OrganizationID = organization.ID
|
||||
};
|
||||
organization.InviteLinks.Add(newInvite);
|
||||
RemotelyContext.SaveChanges();
|
||||
@ -106,6 +105,18 @@ namespace Remotely.Server.Services
|
||||
RemotelyContext.SaveChanges();
|
||||
}
|
||||
|
||||
public bool SetNewUserProperties(string targetName, string organizationID, bool isAdmin)
|
||||
{
|
||||
var targetUser = GetUserByName(targetName);
|
||||
|
||||
targetUser.OrganizationID = organizationID;
|
||||
targetUser.IsAdministrator = isAdmin;
|
||||
|
||||
RemotelyContext.SaveChanges();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool AddOrUpdateDevice(Device device, out Device updatedDevice)
|
||||
{
|
||||
device.LastOnline = DateTime.Now;
|
||||
|
||||
@ -1,15 +1,14 @@
|
||||
import { ShowModal, ValidateInput, PopupMessage } from "../UI.js";
|
||||
import { ShowModal } from "../UI.js";
|
||||
document.getElementById("usersHelpButton").addEventListener("click", (ev) => {
|
||||
ShowModal("Users", `All users for the organization are managed here.<br><br>
|
||||
Administrators will have access to this management screen as well as all computers.`);
|
||||
});
|
||||
document.getElementById("invitesHelpButton").addEventListener("click", (ev) => {
|
||||
ShowModal("Invitations", `All pending invitations will be shown here and can be revoked by deleting them.<br><br>
|
||||
If a user does not exist, sending an invite will create their account and send them a password reset email too.
|
||||
The password reset must be completed before accepting the invitation.
|
||||
If a user does not exist, sending an invite will create their account and add them to the current organization.
|
||||
A password reset URL can be generated from the user table.
|
||||
<br><br>
|
||||
The Admin checkbox determines if the new user will have administrator privileges in this organization
|
||||
after they accept the invitation.`);
|
||||
The Admin checkbox determines if the new user will have administrator privileges in this organization.`);
|
||||
});
|
||||
document.getElementById("deviceGroupHelpButton").addEventListener("click", (ev) => {
|
||||
ShowModal("Device Groups", `Device groups can be used to organize and filter computers on the grid.`);
|
||||
@ -148,57 +147,6 @@ document.querySelectorAll(".delete-user-button").forEach((removeButton) => {
|
||||
}
|
||||
});
|
||||
});
|
||||
document.getElementById("sendInviteButton").addEventListener("click", (ev) => {
|
||||
var inviteUserInput = document.querySelector("#inviteUserInput");
|
||||
if (!ValidateInput(inviteUserInput)) {
|
||||
return;
|
||||
}
|
||||
var invitedUser = inviteUserInput.value;
|
||||
inviteUserInput.value = "";
|
||||
var isAdmin = document.getElementById("inviteIsAdmin").checked;
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onload = () => {
|
||||
if (xhr.status == 200) {
|
||||
var newInvite = JSON.parse(xhr.responseText);
|
||||
var tbody = document.querySelector("#invitesTable tbody");
|
||||
var newRow = document.createElement("tr");
|
||||
newRow.setAttribute("invite", newInvite.ID);
|
||||
var innerHtml = `<td class="middle-aligned"><label class="control-label">${newInvite.InvitedUser}</label></td>
|
||||
<td class="middle-aligned text-center"><input type="checkbox" disabled ${newInvite.IsAdmin ? "checked" : ""}/></td>
|
||||
<td class="middle-aligned">
|
||||
<label class="control-label">
|
||||
<a href="${location.origin}/Invite/?id=${newInvite.ID}">Join Link</a>`;
|
||||
if (newInvite.ResetUrl) {
|
||||
innerHtml += `<br /> <a href="${newInvite.ResetUrl}">Reset Password</a>`;
|
||||
}
|
||||
innerHtml += ` </label> </td>
|
||||
<td><button type="button" class="btn btn-danger delete-invite-button" invite="${newInvite.ID}">Delete</button></td>`;
|
||||
newRow.innerHTML = innerHtml;
|
||||
tbody.appendChild(newRow);
|
||||
newRow.querySelector(".delete-invite-button").addEventListener("click", (ev) => {
|
||||
deleteInvite(ev);
|
||||
});
|
||||
}
|
||||
else if (xhr.status == 400) {
|
||||
ShowModal("Invalid Request", xhr.responseText);
|
||||
}
|
||||
else {
|
||||
showError(xhr);
|
||||
}
|
||||
};
|
||||
xhr.onerror = () => {
|
||||
showError(xhr);
|
||||
};
|
||||
xhr.open("post", location.origin + `/api/OrganizationManagement/SendInvite/`);
|
||||
xhr.setRequestHeader("Content-Type", "application/json");
|
||||
xhr.send(JSON.stringify({ InvitedUser: invitedUser, IsAdmin: isAdmin }));
|
||||
PopupMessage("Sending invite...");
|
||||
});
|
||||
document.getElementById("inviteUserInput").addEventListener("keypress", (e) => {
|
||||
if (e.key.toLowerCase() == "enter") {
|
||||
document.getElementById("sendInviteButton").click();
|
||||
}
|
||||
});
|
||||
document.querySelectorAll(".delete-invite-button").forEach((deleteButton) => {
|
||||
deleteButton.addEventListener("click", (ev) => {
|
||||
deleteInvite(ev);
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -7,11 +7,10 @@ document.getElementById("usersHelpButton").addEventListener("click", (ev) => {
|
||||
});
|
||||
document.getElementById("invitesHelpButton").addEventListener("click", (ev) => {
|
||||
ShowModal("Invitations", `All pending invitations will be shown here and can be revoked by deleting them.<br><br>
|
||||
If a user does not exist, sending an invite will create their account and send them a password reset email too.
|
||||
The password reset must be completed before accepting the invitation.
|
||||
If a user does not exist, sending an invite will create their account and add them to the current organization.
|
||||
A password reset URL can be generated from the user table.
|
||||
<br><br>
|
||||
The Admin checkbox determines if the new user will have administrator privileges in this organization
|
||||
after they accept the invitation.`);
|
||||
The Admin checkbox determines if the new user will have administrator privileges in this organization.`);
|
||||
});
|
||||
|
||||
document.getElementById("deviceGroupHelpButton").addEventListener("click", (ev) => {
|
||||
@ -160,59 +159,6 @@ document.querySelectorAll(".delete-user-button").forEach((removeButton: HTMLButt
|
||||
})
|
||||
});
|
||||
|
||||
document.getElementById("sendInviteButton").addEventListener("click", (ev) => {
|
||||
var inviteUserInput = document.querySelector("#inviteUserInput") as HTMLInputElement;
|
||||
if (!ValidateInput(inviteUserInput)) {
|
||||
return;
|
||||
}
|
||||
var invitedUser = inviteUserInput.value;
|
||||
inviteUserInput.value = "";
|
||||
var isAdmin = (document.getElementById("inviteIsAdmin") as HTMLInputElement).checked;
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onload = () => {
|
||||
if (xhr.status == 200) {
|
||||
var newInvite = JSON.parse(xhr.responseText);
|
||||
var tbody = document.querySelector("#invitesTable tbody");
|
||||
var newRow = document.createElement("tr");
|
||||
newRow.setAttribute("invite", newInvite.ID);
|
||||
var innerHtml = `<td class="middle-aligned"><label class="control-label">${newInvite.InvitedUser}</label></td>
|
||||
<td class="middle-aligned text-center"><input type="checkbox" disabled ${newInvite.IsAdmin ? "checked" : ""}/></td>
|
||||
<td class="middle-aligned">
|
||||
<label class="control-label">
|
||||
<a href="${location.origin}/Invite/?id=${newInvite.ID}">Join Link</a>`;
|
||||
if (newInvite.ResetUrl) {
|
||||
innerHtml += `<br /> <a href="${newInvite.ResetUrl}">Reset Password</a>`;
|
||||
}
|
||||
innerHtml += ` </label> </td>
|
||||
<td><button type="button" class="btn btn-danger delete-invite-button" invite="${newInvite.ID}">Delete</button></td>`;
|
||||
|
||||
newRow.innerHTML = innerHtml;
|
||||
tbody.appendChild(newRow);
|
||||
newRow.querySelector(".delete-invite-button").addEventListener("click", (ev:MouseEvent) => {
|
||||
deleteInvite(ev);
|
||||
})
|
||||
}
|
||||
else if (xhr.status == 400) {
|
||||
ShowModal("Invalid Request", xhr.responseText);
|
||||
}
|
||||
else {
|
||||
showError(xhr);
|
||||
}
|
||||
}
|
||||
xhr.onerror = () => {
|
||||
showError(xhr);
|
||||
}
|
||||
xhr.open("post", location.origin + `/api/OrganizationManagement/SendInvite/`);
|
||||
xhr.setRequestHeader("Content-Type", "application/json");
|
||||
xhr.send(JSON.stringify({ InvitedUser: invitedUser, IsAdmin: isAdmin }));
|
||||
PopupMessage("Sending invite...");
|
||||
});
|
||||
document.getElementById("inviteUserInput").addEventListener("keypress", (e) => {
|
||||
if (e.key.toLowerCase() == "enter") {
|
||||
document.getElementById("sendInviteButton").click();
|
||||
}
|
||||
})
|
||||
|
||||
document.querySelectorAll(".delete-invite-button").forEach((deleteButton: HTMLButtonElement) => {
|
||||
deleteButton.addEventListener("click", (ev) => {
|
||||
deleteInvite(ev);
|
||||
|
||||
@ -10,6 +10,5 @@ namespace Remotely.Shared.ViewModels.Organization
|
||||
public bool IsAdmin { get; set; }
|
||||
public DateTime DateSent { get; set; }
|
||||
public string InvitedUser { get; set; }
|
||||
public string ResetUrl { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user