diff --git a/public/.eslintrc b/public/.eslintrc
index 322f86a92d..2f938bb71a 100644
--- a/public/.eslintrc
+++ b/public/.eslintrc
@@ -27,6 +27,7 @@
"object-shorthand": "off",
"prefer-arrow-callback": "off",
"prefer-spread": "off",
+ "prefer-object-spread": "off",
"prefer-reflect": "off",
"prefer-template": "off"
},
diff --git a/public/language/en-GB/admin/manage/categories.json b/public/language/en-GB/admin/manage/categories.json
index 5d8923c6ac..0c0992fca5 100644
--- a/public/language/en-GB/admin/manage/categories.json
+++ b/public/language/en-GB/admin/manage/categories.json
@@ -66,7 +66,6 @@
"alert.create-success": "Category successfully created!",
"alert.none-active": "You have no active categories.",
"alert.create": "Create a Category",
- "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.",
"alert.confirm-purge": "
Do you really want to purge this category \"%1\"?
Warning! All topics and posts in this category will be purged!
Purging a category will remove all topics and posts, and delete the category from the database. If you want to remove a category temporarily, you'll want to \"disable\" the category instead.
",
"alert.purge-success": "Category purged!",
"alert.copy-success": "Settings Copied!",
diff --git a/public/language/en-GB/admin/manage/privileges.json b/public/language/en-GB/admin/manage/privileges.json
index b97188d214..7295c00103 100644
--- a/public/language/en-GB/admin/manage/privileges.json
+++ b/public/language/en-GB/admin/manage/privileges.json
@@ -1,6 +1,5 @@
{
"global": "Global",
- "global.no-users": "No user-specific global privileges.",
"admin": "Admin",
"group-privileges": "Group Privileges",
"user-privileges": "User Privileges",
@@ -38,5 +37,16 @@
"admin-categories": "Categories",
"admin-privileges": "Privileges",
"admin-users": "Users",
- "admin-settings": "Settings"
+ "admin-settings": "Settings",
+
+ "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.",
+ "alert.confirm-save": "Please confirm your intention to save these privileges",
+ "alert.saved": "Privilege changes saved and applied",
+ "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?",
+ "alert.discarded": "Privilege changes discarded",
+ "alert.confirm-copyToAll": "Are you sure you wish to apply this privilege set to all categories?",
+ "alert.confirm-copyToAllGroup": "Are you sure you wish to apply this group's privilege set to all categories?",
+ "alert.confirm-copyToChildren": "Are you sure you wish to apply this privilege set to all descendant (child) categories?",
+ "alert.confirm-copyToChildrenGroup": "Are you sure you wish to apply this group's privilege set to all descendant (child) categories?",
+ "alert.no-undo": "This action cannot be undone."
}
\ No newline at end of file
diff --git a/public/less/admin/manage/privileges.less b/public/less/admin/manage/privileges.less
index 264d8bb632..e7ed543eea 100644
--- a/public/less/admin/manage/privileges.less
+++ b/public/less/admin/manage/privileges.less
@@ -1,11 +1,3 @@
-.page-manage-privileges {
- .ui-autocomplete {
- height: 500px;
- overflow-y: auto;
- overflow-x: hidden;
- }
-}
-
.page-admin-privileges {
@keyframes fadeOut {
0% {background-color: @brand-primary;}
@@ -16,6 +8,25 @@
animation-name: fadeOut;
animation-duration: 5s;
animation-fill-mode: both;
- animation-timing-function: ease-out;
+ animation-timing-function: ease-out;
+ }
+
+ .privilege-table {
+ td[data-delta="true"] > input {
+ &:after {
+ border-color: @state-success-text;
+ background-color: @state-success-text;
+ }
+ }
+
+ td[data-delta="false"] > input {
+ &:after {
+ border-color: @state-danger-bg;
+ }
+
+ &:indeterminate:after {
+ background-color: @state-danger-bg;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/public/openapi/read.yaml b/public/openapi/read.yaml
index 4b538ded4f..e9e801da7c 100644
--- a/public/openapi/read.yaml
+++ b/public/openapi/read.yaml
@@ -847,6 +847,17 @@ paths:
properties:
name:
type: string
+ keys:
+ type: object
+ properties:
+ users:
+ type: array
+ items:
+ type: string
+ groups:
+ type: array
+ items:
+ type: string
users:
type: array
items:
diff --git a/public/src/admin/manage/privileges.js b/public/src/admin/manage/privileges.js
index 8ce0030285..047701ce75 100644
--- a/public/src/admin/manage/privileges.js
+++ b/public/src/admin/manage/privileges.js
@@ -28,38 +28,72 @@ define('admin/manage/privileges', [
Privileges.setupPrivilegeTable = function () {
$('.privilege-table-container').on('change', 'input[type="checkbox"]', function () {
var checkboxEl = $(this);
- var privilege = checkboxEl.parent().attr('data-privilege');
+ 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/categories:alert.confirm-moderate]]', function (confirm) {
+ bootbox.confirm('[[admin/manage/privileges:alert.confirm-moderate]]', function (confirm) {
if (confirm) {
- Privileges.setPrivilege(member, privilege, state, checkboxEl);
+ wrapperEl.attr('data-delta', delta);
+ Privileges.exposeAssumedPrivileges();
} else {
checkboxEl.prop('checked', !checkboxEl.prop('checked'));
}
});
} else {
- Privileges.setPrivilege(member, privilege, state, checkboxEl);
+ wrapperEl.attr('data-delta', delta);
+ Privileges.exposeAssumedPrivileges();
}
} else {
app.alertError('[[error:invalid-data]]');
}
});
+ document.getElementById('save').addEventListener('click', function () {
+ bootbox.confirm('[[admin/manage/privileges:alert.confirm-save]]', function (ok) {
+ if (ok) {
+ var tableEl = document.querySelector('.privilege-table-container');
+ var requests = tableEl.querySelectorAll('td[data-delta]').forEach(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;
+
+ Privileges.setPrivilege(member, privilege, state);
+ });
+
+ $.when(requests).done(function () {
+ Privileges.refreshPrivilegeTable();
+ app.alertSuccess('[[admin/manage/privileges:alert.saved]]');
+ });
+ }
+ });
+ });
+
+ document.getElementById('discard').addEventListener('click', function () {
+ bootbox.confirm('[[admin/manage/privileges:alert.confirm-discard]]', function (ok) {
+ if (ok) {
+ Privileges.refreshPrivilegeTable();
+ app.alertSuccess('[[admin/manage/privileges:alert.discarded]]');
+ }
+ });
+ });
+
$('.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 () {
- Privileges.copyPrivilegesToChildren(cid, '');
+ 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');
- Privileges.copyPrivilegesToChildren(cid, groupName);
+ throwConfirmModal('copyToChildrenGroup', Privileges.copyPrivilegesToChildren.bind(null, cid, groupName));
});
$('.privilege-table-container').on('click', '[data-action="copyPrivilegesFrom"]', function () {
@@ -71,13 +105,21 @@ define('admin/manage/privileges', [
});
$('.privilege-table-container').on('click', '[data-action="copyToAll"]', function () {
- Privileges.copyPrivilegesToAllCategories(cid, '');
+ 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');
- Privileges.copyPrivilegesToAllCategories(cid, groupName);
+ throwConfirmModal('copyToAllGroup', Privileges.copyPrivilegesToAllCategories.bind(null, cid, groupName));
});
+ function throwConfirmModal(method, onConfirm) {
+ bootbox.confirm('[[admin/manage/privileges:alert.confirm-' + method + ']]
\ No newline at end of file
diff --git a/src/views/admin/partials/privileges/category.tpl b/src/views/admin/partials/privileges/category.tpl
index 1e798e4e5e..99150e05b9 100644
--- a/src/views/admin/partials/privileges/category.tpl
+++ b/src/views/admin/partials/privileges/category.tpl
@@ -52,16 +52,20 @@