'use strict'; define('admin/manage/privileges', [ 'autocomplete', 'translator', 'categorySelector', 'mousetrap', 'admin/modules/checkboxRowSelector', ], function (autocomplete, translator, categorySelector, mousetrap, checkboxRowSelector) { var Privileges = {}; var cid; Privileges.init = function () { cid = isNaN(parseInt(ajaxify.data.selectedCategory.cid, 10)) ? 'admin' : ajaxify.data.selectedCategory.cid; checkboxRowSelector.init('.privilege-table-container'); categorySelector.init($('[component="category-selector"]'), function (category) { cid = parseInt(category.cid, 10); cid = isNaN(cid) ? 'admin' : cid; Privileges.refreshPrivilegeTable(); ajaxify.updateHistory('admin/manage/privileges/' + (cid || '')); }); Privileges.setupPrivilegeTable(); highlightRow(); }; Privileges.setupPrivilegeTable = function () { $('.privilege-table-container').on('change', 'input[type="checkbox"]:not(.checkbox-helper)', function () { var checkboxEl = $(this); var wrapperEl = checkboxEl.parent(); var privilege = wrapperEl.attr('data-privilege'); var state = checkboxEl.prop('checked'); var rowEl = checkboxEl.parents('tr'); var member = rowEl.attr('data-group-name') || rowEl.attr('data-uid'); var isPrivate = parseInt(rowEl.attr('data-private') || 0, 10); var isGroup = rowEl.attr('data-group-name') !== undefined; var delta = checkboxEl.prop('checked') === (wrapperEl.attr('data-value') === 'true') ? null : state; if (member) { if (isGroup && privilege === 'groups:moderate' && !isPrivate && state) { bootbox.confirm('[[admin/manage/privileges:alert.confirm-moderate]]', function (confirm) { if (confirm) { wrapperEl.attr('data-delta', delta); Privileges.exposeAssumedPrivileges(); } else { checkboxEl.prop('checked', !checkboxEl.prop('checked')); } }); } else if (privilege.endsWith('admin:admins-mods') && state) { bootbox.confirm('[[admin/manage/privileges:alert.confirm-admins-mods]]', function (confirm) { if (confirm) { wrapperEl.attr('data-delta', delta); Privileges.exposeAssumedPrivileges(); } else { checkboxEl.prop('checked', !checkboxEl.prop('checked')); } }); } else { wrapperEl.attr('data-delta', delta); Privileges.exposeAssumedPrivileges(); } checkboxRowSelector.updateState(checkboxEl); } else { app.alertError('[[error:invalid-data]]'); } }); Privileges.exposeAssumedPrivileges(); checkboxRowSelector.updateAll(); Privileges.addEvents(); // events with confirmation modals }; Privileges.addEvents = function () { document.getElementById('save').addEventListener('click', function () { throwConfirmModal('save', Privileges.commit); }); document.getElementById('discard').addEventListener('click', function () { throwConfirmModal('discard', Privileges.discard); }); $('.privilege-table-container').on('click', '[data-action="search.user"]', Privileges.addUserToPrivilegeTable); $('.privilege-table-container').on('click', '[data-action="search.group"]', Privileges.addGroupToPrivilegeTable); $('.privilege-table-container').on('click', '[data-action="copyToChildren"]', function () { throwConfirmModal('copyToChildren', Privileges.copyPrivilegesToChildren.bind(null, cid, '')); }); $('.privilege-table-container').on('click', '[data-action="copyToChildrenGroup"]', function () { var groupName = $(this).parents('[data-group-name]').attr('data-group-name'); throwConfirmModal('copyToChildrenGroup', Privileges.copyPrivilegesToChildren.bind(null, cid, groupName)); }); $('.privilege-table-container').on('click', '[data-action="copyPrivilegesFrom"]', function () { Privileges.copyPrivilegesFromCategory(cid, ''); }); $('.privilege-table-container').on('click', '[data-action="copyPrivilegesFromGroup"]', function () { var groupName = $(this).parents('[data-group-name]').attr('data-group-name'); Privileges.copyPrivilegesFromCategory(cid, groupName); }); $('.privilege-table-container').on('click', '[data-action="copyToAll"]', function () { throwConfirmModal('copyToAll', Privileges.copyPrivilegesToAllCategories.bind(null, cid, '')); }); $('.privilege-table-container').on('click', '[data-action="copyToAllGroup"]', function () { var groupName = $(this).parents('[data-group-name]').attr('data-group-name'); throwConfirmModal('copyToAllGroup', Privileges.copyPrivilegesToAllCategories.bind(null, cid, groupName)); }); mousetrap.bind('ctrl+s', function (ev) { throwConfirmModal('save', Privileges.commit); ev.preventDefault(); }); function throwConfirmModal(method, onConfirm) { bootbox.confirm('[[admin/manage/privileges:alert.confirm-' + method + ']]

[[admin/manage/privileges:alert.no-undo]]', function (ok) { if (ok) { onConfirm.call(); } }); } }; Privileges.commit = function () { var tableEl = document.querySelector('.privilege-table-container'); var requests = $.map(tableEl.querySelectorAll('td[data-delta]'), function (el) { var privilege = el.getAttribute('data-privilege'); var rowEl = el.parentNode; var member = rowEl.getAttribute('data-group-name') || rowEl.getAttribute('data-uid'); var state = el.getAttribute('data-delta') === 'true' ? 1 : 0; return Privileges.setPrivilege(member, privilege, state); }); Promise.allSettled(requests).then(function () { Privileges.refreshPrivilegeTable(); app.alertSuccess('[[admin/manage/privileges:alert.saved]]'); }); }; Privileges.discard = function () { Privileges.refreshPrivilegeTable(); app.alertSuccess('[[admin/manage/privileges:alert.discarded]]'); }; Privileges.refreshPrivilegeTable = function (groupToHighlight) { socket.emit('admin.categories.getPrivilegeSettings', cid, function (err, privileges) { if (err) { return app.alertError(err.message); } ajaxify.data.privileges = privileges; var tpl = parseInt(cid, 10) ? 'admin/partials/privileges/category' : 'admin/partials/privileges/global'; app.parseAndTranslate(tpl, { privileges: privileges, }, function (html) { $('.privilege-table-container').html(html); Privileges.exposeAssumedPrivileges(); checkboxRowSelector.updateAll(); hightlightRowByDataAttr('data-group-name', groupToHighlight); }); }); }; Privileges.exposeAssumedPrivileges = function () { /* If registered-users has a privilege enabled, then all users and groups of that privilege should be assumed to have that privilege as well, even if not set in the db, so reflect this arrangement in the table */ var privs = []; $('.privilege-table tr[data-group-name="registered-users"] td input[type="checkbox"]:not(.checkbox-helper)').parent().each(function (idx, el) { if ($(el).find('input').prop('checked')) { privs.push(el.getAttribute('data-privilege')); } }); // Also apply to non-group privileges privs = privs.concat(privs.map(function (priv) { if (priv.startsWith('groups:')) { return priv.slice(7); } return false; })).filter(Boolean); for (var x = 0, numPrivs = privs.length; x < numPrivs; x += 1) { var inputs = $('.privilege-table tr[data-group-name]:not([data-group-name="registered-users"],[data-group-name="guests"],[data-group-name="spiders"]) td[data-privilege="' + privs[x] + '"] input, .privilege-table tr[data-uid] td[data-privilege="' + privs[x] + '"] input'); inputs.each(function (idx, el) { if (!el.checked) { el.indeterminate = true; } }); } }; Privileges.setPrivilege = function (member, privilege, state) { return new Promise(function (resolve, reject) { socket.emit('admin.categories.setPrivilege', { cid: isNaN(cid) ? 0 : cid, privilege: privilege, set: state, member: member, }, function (err) { if (err) { reject(err); return app.alertError(err.message); } resolve(); }); }); }; Privileges.addUserToPrivilegeTable = function () { var modal = bootbox.dialog({ title: '[[admin/manage/categories:alert.find-user]]', message: '', show: true, }); modal.on('shown.bs.modal', function () { var inputEl = modal.find('input'); inputEl.focus(); autocomplete.user(inputEl, function (ev, ui) { addUserToCategory(ui.item.user, function () { modal.modal('hide'); }); }); }); }; Privileges.addGroupToPrivilegeTable = function () { var modal = bootbox.dialog({ title: '[[admin/manage/categories:alert.find-group]]', message: '', show: true, }); modal.on('shown.bs.modal', function () { var inputEl = modal.find('input'); inputEl.focus(); autocomplete.group(inputEl, function (ev, ui) { if (ui.item.group.name === 'administrators') { return app.alert({ type: 'warning', message: '[[admin/manage/privileges:alert.admin-warning]]', }); } addGroupToCategory(ui.item.group.name, function () { modal.modal('hide'); }); }); }); }; Privileges.copyPrivilegesToChildren = function (cid, group) { socket.emit('admin.categories.copyPrivilegesToChildren', { cid: cid, group: group }, function (err) { if (err) { return app.alertError(err.message); } app.alertSuccess('[[admin/manage/categories:privileges.copy-success]]'); }); }; Privileges.copyPrivilegesFromCategory = function (cid, group) { categorySelector.modal(ajaxify.data.categories.slice(1), function (fromCid) { socket.emit('admin.categories.copyPrivilegesFrom', { toCid: cid, fromCid: fromCid, group: group }, function (err) { if (err) { return app.alertError(err.message); } ajaxify.refresh(); }); }); }; Privileges.copyPrivilegesToAllCategories = function (cid, group) { socket.emit('admin.categories.copyPrivilegesToAllCategories', { cid: cid, group: group }, function (err) { if (err) { return app.alertError(err.message); } app.alertSuccess('[[admin/manage/categories:privileges.copy-success]]'); }); }; function hightlightRowByDataAttr(attrName, attrValue) { if (attrValue) { var el = $('[' + attrName + ']').filter(function () { return $(this).attr(attrName) === String(attrValue); }); if (el.length) { el.addClass('selected'); return true; } } return false; } function highlightRow() { if (ajaxify.data.group) { if (hightlightRowByDataAttr('data-group-name', ajaxify.data.group)) { return; } addGroupToCategory(ajaxify.data.group); } } function addGroupToCategory(group, cb) { cb = cb || function () {}; var groupRow = document.querySelector('.privilege-table [data-group-name="' + group + '"]'); if (groupRow) { hightlightRowByDataAttr('data-group-name', group); return cb(); } // Generate data for new row var privilegeSet = ajaxify.data.privileges.keys.groups.reduce(function (memo, cur) { memo[cur] = false; return memo; }, {}); app.parseAndTranslate('admin/partials/privileges/' + ((isNaN(cid) || cid === 0) ? 'global' : 'category'), 'privileges.groups', { privileges: { groups: [ { name: group, nameEscaped: translator.escape(group), privileges: privilegeSet, }, ], }, }, function (html) { var tableEl = document.querySelector('.privilege-table'); var rows = tableEl.querySelectorAll('tbody tr'); html.insertBefore(rows[rows.length - 1]); Privileges.exposeAssumedPrivileges(); hightlightRowByDataAttr('data-group-name', group); cb(); }); } function addUserToCategory(user, cb) { cb = cb || function () {}; var userRow = document.querySelector('.privilege-table [data-uid="' + user.uid + '"]'); if (userRow) { hightlightRowByDataAttr('data-uid', user.uid); return cb(); } // Generate data for new row var privilegeSet = ajaxify.data.privileges.keys.users.reduce(function (memo, cur) { memo[cur] = false; return memo; }, {}); app.parseAndTranslate('admin/partials/privileges/' + (isNaN(cid) ? 'global' : 'category'), 'privileges.users', { privileges: { users: [ { picture: user.picture, username: user.username, uid: user.uid, 'icon:text': user['icon:text'], 'icon:bgColor': user['icon:bgColor'], privileges: privilegeSet, }, ], }, }, function (html) { var tableEl = document.querySelectorAll('.privilege-table'); var rows = tableEl[1].querySelectorAll('tbody tr'); html.insertBefore(rows[rows.length - 1]); hightlightRowByDataAttr('data-uid', user.uid); cb(); }); } return Privileges; });