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:
Harvey Tindall
2020-09-17 23:50:07 +01:00
parent 2b84e45b65
commit 9213f2a078
12 changed files with 289 additions and 4 deletions
+36
View File
@@ -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
View File
@@ -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';
+3 -2
View File
@@ -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;
}
}
+26
View File
@@ -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">&times;</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">