/** * Reset-to-default for site role mapping and feature-access matrix (admin settings). * * @package mod_meetletacbmarketplace * @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) + '§ion=' + 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(); } })();