Files
meetletacbfree/javascript/settings_access_reset.js
T

421 lines
14 KiB
JavaScript

/**
* Reset-to-default for site role mapping and feature-access matrix (admin settings).
*
* @package mod_meetletacbfree
* @copyright 2026 MeetLet
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
(function () {
'use strict';
var modalState = {
open: false,
resolve: null,
lastFocus: null,
onKey: null,
mode: 'confirm',
pendingSection: null,
};
function modalScroll() {
return window.MeetletModalScroll || {
lock: function () {},
unlock: function () {},
focusNoScroll: function (el) {
if (el && typeof el.focus === 'function') {
el.focus();
}
},
};
}
function byId(id) {
return document.getElementById(id);
}
function parseStrings(root) {
if (!root) {
return {};
}
try {
return JSON.parse(root.getAttribute('data-strings') || '{}') || {};
} catch (e) {
return {};
}
}
function sectionContainer(section) {
if (section === 'rolemapping') {
return document.querySelector('[data-meetlet-rolemapping]');
}
if (section === 'featureaccess') {
return document.querySelector('[data-meetlet-access-matrix]');
}
return null;
}
function applyRoleMatrix(container, matrix) {
if (!container || !matrix) {
return;
}
Object.keys(matrix).forEach(function (rowkey) {
var row = container.querySelector('[data-role-row="' + rowkey + '"]');
if (!row) {
return;
}
var val = matrix[rowkey];
var radio = row.querySelector('input[type="radio"][value="' + val + '"]');
if (radio) {
radio.checked = true;
}
});
}
function applyFeatureMatrix(container, matrix) {
if (!container || !matrix) {
return;
}
container.querySelectorAll('.meetlet-access-check').forEach(function (cb) {
var role = cb.getAttribute('data-role');
var feature = cb.getAttribute('data-feature');
var on = matrix[role] && matrix[role][feature];
cb.checked = !!on;
});
}
function renderModalMessage(bodyEl, mode, message) {
if (!bodyEl) {
return;
}
bodyEl.textContent = '';
if (mode === 'busy') {
var wrap = document.createElement('div');
wrap.className = 'meetlet-modal__busy';
var spinner = document.createElement('div');
spinner.className = 'meetlet-modal__spinner';
spinner.setAttribute('aria-hidden', 'true');
var text = document.createElement('p');
text.className = 'meetlet-modal__lead';
text.textContent = message || '';
wrap.appendChild(spinner);
wrap.appendChild(text);
bodyEl.appendChild(wrap);
return;
}
var lead = document.createElement('p');
lead.className = 'meetlet-modal__lead';
lead.textContent = message || '';
bodyEl.appendChild(lead);
}
function closeModal(confirmed) {
var modal = byId('meetlet-access-reset-modal');
if (!modal || !modalState.open) {
return;
}
modal.setAttribute('hidden', 'hidden');
modal.style.display = 'none';
modal.classList.remove('is-busy');
modal.setAttribute('aria-busy', 'false');
modalState.open = false;
modalScroll().unlock();
if (modalState.onKey) {
document.removeEventListener('keydown', modalState.onKey, true);
modalState.onKey = null;
}
var resolve = modalState.resolve;
modalState.resolve = null;
modalState.pendingSection = null;
if (resolve) {
resolve(!!confirmed);
}
if (modalState.lastFocus && typeof modalState.lastFocus.focus === 'function') {
modalScroll().focusNoScroll(modalState.lastFocus);
}
modalState.lastFocus = null;
}
function openModal(opts) {
opts = opts || {};
var mode = opts.mode === 'alert' ? 'alert' : (opts.mode === 'busy' ? 'busy' : 'confirm');
var modal = byId('meetlet-access-reset-modal');
if (!modal) {
return Promise.resolve(false);
}
if (modalState.open) {
if (modalState.mode === 'busy' && mode === 'alert') {
modalState.resolve = null;
} else {
closeModal(false);
}
}
var str = opts.strings || {};
var titleEl = byId('meetlet-access-reset-modal-title');
var bodyEl = byId('meetlet-access-reset-modal-body');
var confirmBtn = byId('meetlet-access-reset-modal-confirm');
var cancelBtn = modal.querySelector('[data-meetlet-modal-dismiss].btn');
var closeBtn = modal.querySelector('.meetlet-modal__close');
if (titleEl) {
titleEl.textContent = opts.title || '';
}
renderModalMessage(bodyEl, mode, opts.message || '');
modal.classList.toggle('is-busy', mode === 'busy');
modal.setAttribute('aria-busy', mode === 'busy' ? 'true' : 'false');
if (confirmBtn) {
confirmBtn.textContent = opts.confirmLabel || (mode === 'alert' ? (str.modal_ok || 'OK') : (str.confirm_reset || 'Reset'));
confirmBtn.className = opts.confirmClass || (mode === 'alert' ? 'btn btn-primary' : 'btn btn-outline-danger');
confirmBtn.hidden = mode === 'busy';
confirmBtn.disabled = mode === 'busy';
confirmBtn.style.display = mode === 'busy' ? 'none' : '';
}
if (cancelBtn) {
cancelBtn.textContent = str.modal_cancel || 'Cancel';
cancelBtn.hidden = mode !== 'confirm';
cancelBtn.style.display = mode !== 'confirm' ? 'none' : '';
}
if (closeBtn) {
if (str.modal_close) {
closeBtn.setAttribute('aria-label', str.modal_close);
}
closeBtn.hidden = mode === 'busy';
closeBtn.style.display = mode === 'busy' ? 'none' : '';
}
var dir = document.documentElement.getAttribute('dir') || 'ltr';
modal.setAttribute('dir', dir);
var dialog = modal.querySelector('.meetlet-modal__dialog');
if (dialog) {
dialog.setAttribute('dir', dir);
}
modalState.mode = mode;
if (!modalState.lastFocus) {
modalState.lastFocus = document.activeElement;
}
modalScroll().lock();
modal.removeAttribute('hidden');
modal.style.display = 'flex';
modalState.open = true;
if (modalState.onKey) {
document.removeEventListener('keydown', modalState.onKey, true);
}
modalState.onKey = function (e) {
if (!modalState.open) {
return;
}
if (e.key === 'Escape') {
if (modalState.mode === 'busy') {
e.preventDefault();
e.stopPropagation();
return;
}
e.preventDefault();
e.stopPropagation();
closeModal(false);
return;
}
if (e.key !== 'Tab' || modalState.mode === 'busy') {
return;
}
var focusable = modal.querySelectorAll(
'button:not([disabled]):not([hidden]), [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
var visible = [];
for (var i = 0; i < focusable.length; i++) {
if (focusable[i].style.display === 'none') {
continue;
}
visible.push(focusable[i]);
}
if (!visible.length) {
return;
}
var first = visible[0];
var last = visible[visible.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
modalScroll().focusNoScroll(last);
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
modalScroll().focusNoScroll(first);
}
};
document.addEventListener('keydown', modalState.onKey, true);
if (mode !== 'busy' && confirmBtn && !confirmBtn.hidden) {
window.setTimeout(function () {
modalScroll().focusNoScroll(confirmBtn);
}, 20);
}
if (mode === 'busy') {
modalState.resolve = null;
return Promise.resolve(true);
}
return new Promise(function (resolve) {
modalState.resolve = resolve;
});
}
function bindModal() {
var modal = byId('meetlet-access-reset-modal');
if (!modal || modal.dataset.bound === '1') {
return;
}
modal.dataset.bound = '1';
modal.addEventListener('click', function (e) {
if (modalState.mode === 'busy') {
e.preventDefault();
return;
}
if (e.target.closest('[data-meetlet-modal-dismiss]')) {
e.preventDefault();
closeModal(false);
}
});
var confirmBtn = byId('meetlet-access-reset-modal-confirm');
if (confirmBtn) {
confirmBtn.addEventListener('click', function (e) {
e.preventDefault();
if (modalState.mode === 'busy') {
return;
}
closeModal(true);
});
}
}
function postReset(root, section, str) {
var endpoint = root.getAttribute('data-endpoint') || '';
var sesskey = root.getAttribute('data-sesskey') || '';
if (!endpoint || !sesskey) {
return Promise.reject(new Error(str.error || 'Error'));
}
var url = endpoint + (endpoint.indexOf('?') >= 0 ? '&' : '?') +
'sesskey=' + encodeURIComponent(sesskey) +
'&section=' + encodeURIComponent(section);
return fetch(url, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Accept': 'application/json',
},
}).then(function (resp) {
return resp.json();
});
}
function confirmCopy(section, str) {
if (section === 'rolemapping') {
return {
title: str.confirm_rolemapping_title || '',
message: str.confirm_rolemapping || '',
};
}
return {
title: str.confirm_featureaccess_title || '',
message: str.confirm_featureaccess || '',
};
}
function successCopy(section, str, serverMessage) {
if (serverMessage) {
return serverMessage;
}
if (section === 'rolemapping') {
return str.success_rolemapping || '';
}
return str.success_featureaccess || '';
}
function handleResetClick(btn, root, str) {
var section = btn.getAttribute('data-meetlet-reset-section') || '';
if (!section) {
return;
}
var copy = confirmCopy(section, str);
openModal({
mode: 'confirm',
strings: str,
title: copy.title,
message: copy.message,
confirmClass: 'btn btn-outline-danger',
}).then(function (confirmed) {
if (!confirmed) {
return;
}
openModal({
mode: 'busy',
strings: str,
title: str.busy || '',
message: str.busy || '',
});
btn.disabled = true;
postReset(root, section, str).then(function (data) {
btn.disabled = false;
if (!data || !data.success) {
openModal({
mode: 'alert',
strings: str,
title: str.error || 'Error',
message: (data && data.error) || str.error || 'Error',
confirmClass: 'btn btn-primary',
});
return;
}
var container = sectionContainer(section);
if (section === 'rolemapping') {
applyRoleMatrix(container, data.matrix);
} else {
applyFeatureMatrix(container, data.matrix);
}
openModal({
mode: 'alert',
strings: str,
title: copy.title,
message: successCopy(section, str, data.message),
confirmClass: 'btn btn-primary',
});
}).catch(function () {
btn.disabled = false;
openModal({
mode: 'alert',
strings: str,
title: str.error || 'Error',
message: str.error || 'Error',
confirmClass: 'btn btn-primary',
});
});
});
}
function init() {
var root = byId('meetlet-access-reset-root');
if (!root) {
return;
}
var str = parseStrings(root);
bindModal();
document.addEventListener('click', function (e) {
var btn = e.target.closest('[data-meetlet-reset-section]');
if (!btn || btn.disabled) {
return;
}
e.preventDefault();
handleResetClick(btn, root, str);
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();