diff --git a/public/openapi/write.yaml b/public/openapi/write.yaml index 7fade16fab..b28e265a4b 100644 --- a/public/openapi/write.yaml +++ b/public/openapi/write.yaml @@ -92,6 +92,8 @@ paths: $ref: 'write/categories/cid/privileges.yaml' /categories/{cid}/privileges/{privilege}: $ref: 'write/categories/cid/privileges/privilege.yaml' + /categories/{cid}/moderator/{uid}: + $ref: 'write/categories/cid/moderator/uid.yaml' /topics/: $ref: 'write/topics.yaml' /topics/{tid}: diff --git a/public/openapi/write/categories/cid/moderator/uid.yaml b/public/openapi/write/categories/cid/moderator/uid.yaml new file mode 100644 index 0000000000..4dc5e576ae --- /dev/null +++ b/public/openapi/write/categories/cid/moderator/uid.yaml @@ -0,0 +1,64 @@ +put: + tags: + - categories + summary: Make a user moderator of category + description: This operation makes a user the moderator of a specific category + parameters: + - in: path + name: cid + schema: + type: string + required: true + description: a valid category id + example: 1 + - in: path + name: uid + schema: + type: string + required: true + description: The uid of the user that will be the moderator + example: 2 + responses: + '200': + description: User successfully made moderator + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../../../../components/schemas/Status.yaml#/Status + response: + type: object +delete: + tags: + - categories + summary: Remove a category moderator + description: This operation removes a user from category moderators + parameters: + - in: path + name: cid + schema: + type: string + required: true + description: a valid category id + example: 1 + - in: path + name: uid + schema: + type: string + required: true + description: The uid of the user that will be removed from moderators + example: 2 + responses: + '200': + description: User successfully made moderator + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../../../../components/schemas/Status.yaml#/Status + response: + type: object \ No newline at end of file diff --git a/public/src/admin/manage/admins-mods.js b/public/src/admin/manage/admins-mods.js index 9a70178c71..d59cbba305 100644 --- a/public/src/admin/manage/admins-mods.js +++ b/public/src/admin/manage/admins-mods.js @@ -87,12 +87,7 @@ define('admin/manage/admins-mods', [ autocomplete.user($('.moderator-search'), function (ev, ui) { const input = $(ev.target); const cid = $(ev.target).attr('data-cid'); - socket.emit('admin.categories.setPrivilege', { - cid: cid, - privilege: ajaxify.data.allPrivileges, - set: true, - member: ui.item.user.uid, - }, function (err) { + api.put(`/categories/${cid}/moderator/${ui.item.user.uid}`, {}, function (err) { if (err) { return alerts.error(err); } @@ -118,12 +113,7 @@ define('admin/manage/admins-mods', [ bootbox.confirm('[[admin/manage/users:alerts.confirm-remove-moderator]]', function (confirm) { if (confirm) { - socket.emit('admin.categories.setPrivilege', { - cid: cid, - privilege: ajaxify.data.allPrivileges, - set: false, - member: uid, - }, function (err) { + api.delete(`/categories/${cid}/moderator/${uid}`, {}, function (err) { if (err) { return alerts.error(err); } diff --git a/src/controllers/admin/admins-mods.js b/src/controllers/admin/admins-mods.js index be5c79d1d5..0bb5a936d1 100644 --- a/src/controllers/admin/admins-mods.js +++ b/src/controllers/admin/admins-mods.js @@ -19,11 +19,10 @@ AdminsMods.get = async function (req, res, next) { if (!selectedCategory) { return next(); } - const [admins, globalMods, moderators, categoryPrivList] = await Promise.all([ + const [admins, globalMods, moderators] = await Promise.all([ groups.get('administrators', { uid: req.uid }), groups.get('Global Moderators', { uid: req.uid }), getModeratorsOfCategories(selectedCategory), - privileges.categories.getUserPrivilegeList(), ]); res.render('admin/manage/admins-mods', { @@ -31,7 +30,6 @@ AdminsMods.get = async function (req, res, next) { globalMods: globalMods, categoryMods: [moderators], selectedCategory: selectedCategory, - allPrivileges: categoryPrivList, }); }; diff --git a/src/controllers/write/categories.js b/src/controllers/write/categories.js index b4214068b7..22250b931c 100644 --- a/src/controllers/write/categories.js +++ b/src/controllers/write/categories.js @@ -66,3 +66,17 @@ Categories.setPrivilege = async (req, res) => { const privilegeSet = await api.categories.getPrivileges(req, req.params.cid); helpers.formatApiResponse(200, res, privilegeSet); }; + +Categories.setModerator = async (req, res) => { + if (!await privileges.admin.can('admin:admins-mods', req.uid)) { + throw new Error('[[error:no-privileges]]'); + } + const privilegeList = await privileges.categories.getUserPrivilegeList(); + await api.categories.setPrivilege(req, { + cid: req.params.cid, + privilege: privilegeList, + member: req.params.uid, + set: req.method === 'PUT', + }); + helpers.formatApiResponse(200, res); +}; diff --git a/src/privileges/admin.js b/src/privileges/admin.js index cea44886a3..3cf42fa7a8 100644 --- a/src/privileges/admin.js +++ b/src/privileges/admin.js @@ -77,9 +77,6 @@ privsAdmin.socketMap = { 'admin.analytics.get': 'admin:dashboard', 'admin.categories.copySettingsFrom': 'admin:categories', - - 'admin.categories.getPrivilegeSettings': 'admin:privileges', - 'admin.categories.setPrivilege': 'admin:privileges;admin:admins-mods', 'admin.categories.copyPrivilegesToChildren': 'admin:privileges', 'admin.categories.copyPrivilegesFrom': 'admin:privileges', 'admin.categories.copyPrivilegesToAllCategories': 'admin:privileges', diff --git a/src/routes/write/categories.js b/src/routes/write/categories.js index db8492dba4..ed3ffd2dce 100644 --- a/src/routes/write/categories.js +++ b/src/routes/write/categories.js @@ -19,5 +19,8 @@ module.exports = function () { setupApiRoute(router, 'put', '/:cid/privileges/:privilege', [...middlewares, middleware.checkRequired.bind(null, ['member'])], controllers.write.categories.setPrivilege); setupApiRoute(router, 'delete', '/:cid/privileges/:privilege', [...middlewares, middleware.checkRequired.bind(null, ['member'])], controllers.write.categories.setPrivilege); + setupApiRoute(router, 'put', '/:cid/moderator/:uid', [...middlewares], controllers.write.categories.setModerator); + setupApiRoute(router, 'delete', '/:cid/moderator/:uid', [...middlewares], controllers.write.categories.setModerator); + return router; }; diff --git a/src/socket.io/admin/categories.js b/src/socket.io/admin/categories.js index 2360e29da8..53c541598d 100644 --- a/src/socket.io/admin/categories.js +++ b/src/socket.io/admin/categories.js @@ -2,8 +2,6 @@ const categories = require('../../categories'); -const api = require('../../api'); -const sockets = require('..'); const Categories = module.exports; @@ -11,24 +9,6 @@ Categories.getNames = async function () { return await categories.getAllCategoryFields(['cid', 'name']); }; -Categories.setPrivilege = async function (socket, data) { - sockets.warnDeprecated(socket, 'PUT /api/v3/categories/:cid/privileges/:privilege'); - - if (!data) { - throw new Error('[[error:invalid-data]]'); - } - return await api.categories.setPrivilege(socket, data); -}; - -Categories.getPrivilegeSettings = async function (socket, cid) { - sockets.warnDeprecated(socket, 'GET /api/v3/categories/:cid/privileges'); - - if (!isFinite(cid) && cid !== 'admin') { - throw new Error('[[error:invalid-data]]'); - } - return await api.categories.getPrivileges(socket, cid); -}; - Categories.copyPrivilegesToChildren = async function (socket, data) { const result = await categories.getChildren([data.cid], socket.uid); const children = result[0]; diff --git a/test/categories.js b/test/categories.js index 979f1c7593..e7bca1a40c 100644 --- a/test/categories.js +++ b/test/categories.js @@ -468,67 +468,43 @@ describe('Categories', () => { }); }); - it('should give privilege', (done) => { - socketCategories.setPrivilege({ uid: adminUid }, { cid: categoryObj.cid, privilege: ['groups:topics:delete'], set: true, member: 'registered-users' }, (err) => { - assert.ifError(err); - privileges.categories.can('topics:delete', categoryObj.cid, posterUid, (err, canDeleteTopcis) => { - assert.ifError(err); - assert(canDeleteTopcis); - done(); - }); - }); + it('should give privilege', async () => { + await apiCategories.setPrivilege({ uid: adminUid }, { cid: categoryObj.cid, privilege: ['groups:topics:delete'], set: true, member: 'registered-users' }); + const canDeleteTopics = await privileges.categories.can('topics:delete', categoryObj.cid, posterUid); + assert(canDeleteTopics); }); - it('should remove privilege', (done) => { - socketCategories.setPrivilege({ uid: adminUid }, { cid: categoryObj.cid, privilege: 'groups:topics:delete', set: false, member: 'registered-users' }, (err) => { - assert.ifError(err); - privileges.categories.can('topics:delete', categoryObj.cid, posterUid, (err, canDeleteTopcis) => { - assert.ifError(err); - assert(!canDeleteTopcis); - done(); - }); - }); + it('should remove privilege', async () => { + await apiCategories.setPrivilege({ uid: adminUid }, { cid: categoryObj.cid, privilege: 'groups:topics:delete', set: false, member: 'registered-users' }); + const canDeleteTopics = await privileges.categories.can('topics:delete', categoryObj.cid, posterUid); + assert(!canDeleteTopics); }); - it('should get privilege settings', (done) => { - socketCategories.getPrivilegeSettings({ uid: adminUid }, categoryObj.cid, (err, data) => { - assert.ifError(err); - assert(data); - done(); - }); + it('should get privilege settings', async () => { + const data = await apiCategories.getPrivileges({ uid: adminUid }, categoryObj.cid); + assert(data.labels); + assert(data.labels.users); + assert(data.labels.groups); + assert(data.keys.users); + assert(data.keys.groups); + assert(data.users); + assert(data.groups); }); - it('should copy privileges to children', (done) => { - let parentCid; - let child1Cid; - let child2Cid; - async.waterfall([ - function (next) { - Categories.create({ name: 'parent' }, next); - }, - function (category, next) { - parentCid = category.cid; - Categories.create({ name: 'child1', parentCid: parentCid }, next); - }, - function (category, next) { - child1Cid = category.cid; - Categories.create({ name: 'child2', parentCid: child1Cid }, next); - }, - function (category, next) { - child2Cid = category.cid; - socketCategories.setPrivilege({ uid: adminUid }, { cid: parentCid, privilege: 'groups:topics:delete', set: true, member: 'registered-users' }, next); - }, - function (next) { - socketCategories.copyPrivilegesToChildren({ uid: adminUid }, { cid: parentCid, group: '' }, next); - }, - function (next) { - privileges.categories.can('topics:delete', child2Cid, posterUid, next); - }, - function (canDelete, next) { - assert(canDelete); - next(); - }, - ], done); + it('should copy privileges to children', async () => { + const parentCategory = await Categories.create({ name: 'parent' }); + const parentCid = parentCategory.cid; + const child1 = await Categories.create({ name: 'child1', parentCid: parentCid }); + const child2 = await Categories.create({ name: 'child2', parentCid: child1.cid }); + await apiCategories.setPrivilege({ uid: adminUid }, { + cid: parentCid, + privilege: 'groups:topics:delete', + set: true, + member: 'registered-users', + }); + await socketCategories.copyPrivilegesToChildren({ uid: adminUid }, { cid: parentCid, group: '' }); + const canDelete = await privileges.categories.can('topics:delete', child2.cid, posterUid); + assert(canDelete); }); it('should create category with settings from', (done) => { @@ -579,60 +555,34 @@ describe('Categories', () => { ], done); }); - it('should copy privileges from another category', (done) => { - let child1Cid; - let parentCid; - async.waterfall([ - function (next) { - Categories.create({ name: 'parent', description: 'copy me' }, next); - }, - function (category, next) { - parentCid = category.cid; - Categories.create({ name: 'child1' }, next); - }, - function (category, next) { - child1Cid = category.cid; - socketCategories.setPrivilege({ uid: adminUid }, { cid: parentCid, privilege: 'groups:topics:delete', set: true, member: 'registered-users' }, next); - }, - function (next) { - socketCategories.copyPrivilegesFrom({ uid: adminUid }, { fromCid: parentCid, toCid: child1Cid }, next); - }, - function (next) { - privileges.categories.can('topics:delete', child1Cid, posterUid, next); - }, - function (canDelete, next) { - assert(canDelete); - next(); - }, - ], done); - }); - - it('should copy privileges from another category for a single group', (done) => { - let child1Cid; - let parentCid; - async.waterfall([ - function (next) { - Categories.create({ name: 'parent', description: 'copy me' }, next); - }, - function (category, next) { - parentCid = category.cid; - Categories.create({ name: 'child1' }, next); - }, - function (category, next) { - child1Cid = category.cid; - socketCategories.setPrivilege({ uid: adminUid }, { cid: parentCid, privilege: 'groups:topics:delete', set: true, member: 'registered-users' }, next); - }, - function (next) { - socketCategories.copyPrivilegesFrom({ uid: adminUid }, { fromCid: parentCid, toCid: child1Cid, group: 'registered-users' }, next); - }, - function (next) { - privileges.categories.can('topics:delete', child1Cid, 0, next); - }, - function (canDelete, next) { - assert(!canDelete); - next(); - }, - ], done); + it('should copy privileges from another category', async () => { + const parent = await Categories.create({ name: 'parent', description: 'copy me' }); + const parentCid = parent.cid; + const child1 = await Categories.create({ name: 'child1' }); + await apiCategories.setPrivilege({ uid: adminUid }, { + cid: parentCid, + privilege: 'groups:topics:delete', + set: true, + member: 'registered-users', + }); + await socketCategories.copyPrivilegesFrom({ uid: adminUid }, { fromCid: parentCid, toCid: child1.cid }); + const canDelete = await privileges.categories.can('topics:delete', child1.cid, posterUid); + assert(canDelete); + }); + + it('should copy privileges from another category for a single group', async () => { + const parent = await Categories.create({ name: 'parent', description: 'copy me' }); + const parentCid = parent.cid; + const child1 = await Categories.create({ name: 'child1' }); + await apiCategories.setPrivilege({ uid: adminUid }, { + cid: parentCid, + privilege: 'groups:topics:delete', + set: true, + member: 'registered-users', + }); + await socketCategories.copyPrivilegesFrom({ uid: adminUid }, { fromCid: parentCid, toCid: child1.cid, group: 'registered-users' }); + const canDelete = await privileges.categories.can('topics:delete', child1.cid, 0); + assert(!canDelete); }); });