Add account deletion with email notification
Select users to delete, then optionally opt to notify the user in an email with a provided reason.
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
"mailgun",
|
||||
"smtp",
|
||||
"ombi",
|
||||
"deletion",
|
||||
"files"
|
||||
],
|
||||
"jellyfin": {
|
||||
@@ -634,6 +635,41 @@
|
||||
"description": "API Key. Get this from the first tab in Ombi settings."
|
||||
}
|
||||
},
|
||||
"deletion": {
|
||||
"order": [
|
||||
"subject",
|
||||
"email_html",
|
||||
"email_text"
|
||||
],
|
||||
"meta": {
|
||||
"name": "Account Deletion",
|
||||
"description": "Subject/email files for account deletion emails."
|
||||
},
|
||||
"subject": {
|
||||
"name": "Email subject",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "text",
|
||||
"value": "Your account was deleted - Jellyfin",
|
||||
"description": "Subject of account deletion emails."
|
||||
},
|
||||
"email_html": {
|
||||
"name": "Custom email (HTML)",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Path to custom email html"
|
||||
},
|
||||
"email_text": {
|
||||
"name": "Custom email (plaintext)",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Path to custom email in plain text"
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"order": [
|
||||
"invites",
|
||||
|
||||
+68
-2
@@ -34,6 +34,73 @@ function checkCheckboxes() {
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('deleteModalNotify').onclick = function() {
|
||||
const textbox = document.getElementById('deleteModalReasonBox');
|
||||
if (this.checked && textbox.classList.contains('unfocused')) {
|
||||
textbox.classList.remove('unfocused');
|
||||
} else if (!this.checked) {
|
||||
textbox.classList.add('unfocused');
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById('accountsTabDelete').onclick = function() {
|
||||
const deleteButton = this;
|
||||
let selected = [];
|
||||
const checkboxes = document.getElementById('accountsList').querySelectorAll('input[type=checkbox]');
|
||||
for (check of checkboxes) {
|
||||
if (check.checked) {
|
||||
selected.push(check.id.replace('select_', ''));
|
||||
}
|
||||
}
|
||||
let title = " user";
|
||||
if (selected.length > 1) {
|
||||
title += "s";
|
||||
}
|
||||
title = "Delete " + selected.length + title;
|
||||
document.getElementById('deleteModalTitle').textContent = title;
|
||||
document.getElementById('deleteModalNotify').checked = false;
|
||||
document.getElementById('deleteModalReason').value = '';
|
||||
document.getElementById('deleteModalReasonBox').classList.add('unfocused');
|
||||
document.getElementById('deleteModalSend').textContent = 'Delete';
|
||||
|
||||
document.getElementById('deleteModalSend').onclick = function() {
|
||||
const button = this;
|
||||
const send = {
|
||||
'users': selected,
|
||||
'notify': document.getElementById('deleteModalNotify').checked,
|
||||
'reason': document.getElementById('deleteModalReason').value
|
||||
};
|
||||
let req = new XMLHttpRequest();
|
||||
req.open("POST", "/deleteUser", true);
|
||||
req.responseType = 'json';
|
||||
req.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
|
||||
req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||
req.onreadystatechange = function() {
|
||||
if (this.readyState == 4) {
|
||||
if (this.status == 500) {
|
||||
if ("error" in req.response) {
|
||||
button.textContent = 'Failed';
|
||||
} else {
|
||||
button.textContent = 'Partial fail (check console)';
|
||||
console.log(req.response);
|
||||
}
|
||||
setTimeout(function() {
|
||||
deleteModal.hide();
|
||||
deleteButton.classList.add('unfocused');
|
||||
}, 4000);
|
||||
} else {
|
||||
deleteButton.classList.add('unfocused');
|
||||
deleteModal.hide();
|
||||
}
|
||||
populateUsers();
|
||||
checkCheckboxes();
|
||||
}
|
||||
};
|
||||
req.send(JSON.stringify(send));
|
||||
};
|
||||
deleteModal.show();
|
||||
}
|
||||
|
||||
var jfUsers = [];
|
||||
|
||||
function validEmail(email) {
|
||||
@@ -68,7 +135,6 @@ function changeEmail(icon, id) {
|
||||
//this.remove();
|
||||
let send = {};
|
||||
send[id] = newEmail;
|
||||
console.log(send);
|
||||
let req = new XMLHttpRequest();
|
||||
req.open("POST", "/modifyEmails", true);
|
||||
req.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
|
||||
@@ -195,7 +261,7 @@ document.getElementById('accountsTabSetDefaults').onclick = function() {
|
||||
checked = '';
|
||||
}
|
||||
radio.innerHTML = `
|
||||
<label><input type="radio" name="defaultRadios" id="select_${user['id']}" style="margin-right: 1rem;" ${checked}>${user['name']}</label>`;
|
||||
<label><input type="radio" name="defaultRadios" id="default_${user['id']}" style="margin-right: 1rem;" ${checked}>${user['name']}</label>`;
|
||||
radioList.appendChild(radio);
|
||||
}
|
||||
let userstring = 'user';
|
||||
|
||||
@@ -134,6 +134,7 @@ var usersModal = createModal('users');
|
||||
var restartModal = createModal('restartModal');
|
||||
var refreshModal = createModal('refreshModal');
|
||||
var aboutModal = createModal('aboutModal');
|
||||
var deleteModal = createModal('deleteModal');
|
||||
|
||||
// Parsed invite: [<code>, <expires in _>, <1: Empty invite (no delete/link), 0: Actual invite>, <email address>, <remaining uses>, [<used-by>], <date created>, <notify on expiry>, <notify on creation>]
|
||||
function parseInvite(invite, empty = false) {
|
||||
@@ -671,7 +672,7 @@ document.getElementById('openDefaultsWizard').onclick = function() {
|
||||
checked = '';
|
||||
}
|
||||
radio.innerHTML =
|
||||
`<label><input type="radio" name="defaultRadios" id="select_${user['id']}" style="margin-right: 1rem;" ${checked}>${user['name']}</label>`;
|
||||
`<label><input type="radio" name="defaultRadios" id="default_${user['id']}" style="margin-right: 1rem;" ${checked}>${user['name']}</label>`;
|
||||
radioList.appendChild(radio);
|
||||
}
|
||||
let button = document.getElementById('openDefaultsWizard');
|
||||
@@ -718,7 +719,7 @@ function storeDefaults(users) {
|
||||
let id = '';
|
||||
for (let radio of radios) {
|
||||
if (radio.checked) {
|
||||
id = radio.id.replace('select_', '');
|
||||
id = radio.id.replace('default_', '');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,6 +244,32 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="deleteModal" role="dialog" aria-labelledby="Account deletion" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="deleteModalTitle"></h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="" id="deleteModalNotify">
|
||||
<label class="form-check-label" for="deleteModalNotify">Notify users of account deletion</label>
|
||||
</div>
|
||||
<div class="mb-3 unfocused" id="deleteModalReasonBox">
|
||||
<label for="deleteModalReason" class="form-label">Reason for deletion</label>
|
||||
<textarea class="form-control" id="deleteModalReason" rows="2"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-danger" id="deleteModalSend">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pageContainer">
|
||||
<h1><a id="invitesTabButton" class="text-button">invites </a><a id="accountsTabButton" class="text-button text-muted">accounts</a></h1>
|
||||
<div class="btn-group" role="group" id="headerButtons">
|
||||
|
||||
Reference in New Issue
Block a user