Files
itflow/agent/js/client_export.js

113 lines
5.0 KiB
JavaScript

// Export a client's data (agent/modals/client/client_export_data.php).
//
// Each form: All / None for the sections; the passwords switch only shows
// while Credentials is ticked, and the password pair only while the switch
// is on (disabled otherwise, so a hidden field never blocks the submit).
// A download does not leave the page, so the button shows it is working
// until the server's itflow_download cookie says the file is on its way.
// Share a link carries the ticked parts to the share window in its address.
//
// Wrapped in an IIFE because the modal can be opened again in one page load.
(function () {
const root = document.getElementById('clientExport');
if (!root) {
return;
}
function cookie(name) {
const match = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;]*)'));
return match ? decodeURIComponent(match[1]) : '';
}
root.querySelectorAll('form[data-client-export]').forEach(function (form) {
const credentials = form.querySelector('[data-client-export-credentials]');
const secretsRow = form.querySelector('[data-client-export-secrets-row]');
const secrets = form.querySelector('[data-client-export-secrets]');
const passwordBox = form.querySelector('[data-client-export-password]');
const passwords = passwordBox ? passwordBox.querySelectorAll('input[type=password]') : [];
const submit = form.querySelector('[data-client-export-submit]');
const share = form.querySelector('[data-client-export-share]');
const shareUrl = share ? share.dataset.modalUrl : '';
function sync() {
// Share a link opens the share window with the same parts ticked
if (share) {
const picked = Array.from(form.querySelectorAll('input[name="sections[]"]:checked')).map(function (box) {
return 'sections[]=' + encodeURIComponent(box.value);
});
share.dataset.modalUrl = shareUrl + (picked.length ? '&' + picked.join('&') : '&sections[]=');
}
const canSecrets = !!(credentials && credentials.checked);
if (secretsRow) {
secretsRow.hidden = !canSecrets;
}
const wantSecrets = canSecrets && secrets && secrets.checked;
if (passwordBox) {
passwordBox.hidden = !wantSecrets;
}
passwords.forEach(function (field) {
field.disabled = !wantSecrets;
field.required = wantSecrets;
});
}
form.addEventListener('change', sync);
sync();
form.querySelectorAll('[data-client-export-all], [data-client-export-none]').forEach(function (link) {
link.addEventListener('click', function (e) {
e.preventDefault();
const on = link.hasAttribute('data-client-export-all');
form.querySelectorAll('input[name="sections[]"]').forEach(function (box) {
box.checked = on;
});
sync();
});
});
// The two passwords have to match before anything is sent
if (passwords.length === 2) {
passwords[1].addEventListener('input', function () {
passwords[1].setCustomValidity(passwords[1].value === passwords[0].value ? '' : 'The passwords do not match');
});
passwords[0].addEventListener('input', function () {
if (passwords[1].value !== '') {
passwords[1].setCustomValidity(passwords[1].value === passwords[0].value ? '' : 'The passwords do not match');
}
});
}
form.addEventListener('submit', function () {
const token = Math.random().toString(36).slice(2) + Date.now().toString(36);
form.querySelector('input[name="download_token"]').value = token;
const label = submit.innerHTML;
submit.disabled = true;
submit.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>Preparing...';
// A disabled button is left out of the post - carry its name across
let marker = form.querySelector('input[data-client-export-trigger]');
if (!marker) {
marker = document.createElement('input');
marker.type = 'hidden';
marker.setAttribute('data-client-export-trigger', '');
form.appendChild(marker);
}
marker.name = submit.name;
marker.value = submit.value;
const started = Date.now();
const timer = setInterval(function () {
// Done when the file arrives - or give up after ten minutes and let them try again
if (cookie('itflow_download') === token || Date.now() - started > 600000) {
clearInterval(timer);
submit.disabled = false;
submit.innerHTML = label;
}
}, 500);
});
});
})();