mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 08:36:12 +01:00
feat: option to restrict group leaving, closes #7770
This commit is contained in:
@@ -25,7 +25,8 @@
|
||||
"edit.show-badge": "Show Badge",
|
||||
"edit.private-details": "If enabled, joining of groups requires approval from a group owner.",
|
||||
"edit.private-override": "Warning: Private groups is disabled at system level, which overrides this option.",
|
||||
"edit.disable-requests": "Disable join requests",
|
||||
"edit.disable-join": "Disable join requests",
|
||||
"edit.disable-leave": "Disallow users from leaving the group",
|
||||
"edit.hidden": "Hidden",
|
||||
"edit.hidden-details": "If enabled, this group will not be found in the groups listing, and users will have to be invited manually",
|
||||
"edit.add-user": "Add User to Group",
|
||||
|
||||
@@ -117,6 +117,8 @@
|
||||
"group-needs-owner": "This group requires at least one owner",
|
||||
"group-already-invited": "This user has already been invited",
|
||||
"group-already-requested": "Your membership request has already been submitted",
|
||||
"group-join-disabled": "You are not able to join this group at this time",
|
||||
"group-leave-disabled": "You are not able to leave this group at this time",
|
||||
|
||||
"post-already-deleted": "This post has already been deleted",
|
||||
"post-already-restored": "This post has already been restored",
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"details.latest_posts": "Latest Posts",
|
||||
"details.private": "Private",
|
||||
"details.disableJoinRequests": "Disable join requests",
|
||||
"details.disableLeave": "Disallow users from leaving the group",
|
||||
"details.grant": "Grant/Rescind Ownership",
|
||||
"details.kick": "Kick",
|
||||
"details.kick_confirm": "Are you sure you want to remove this member from the group?",
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
// Groups helpers
|
||||
function membershipBtn(groupObj) {
|
||||
if (groupObj.isMember && groupObj.name !== 'administrators') {
|
||||
return '<button class="btn btn-danger" data-action="leave" data-group="' + groupObj.displayName + '"><i class="fa fa-times"></i> [[groups:membership.leave-group]]</button>';
|
||||
return '<button class="btn btn-danger" data-action="leave" data-group="' + groupObj.displayName + '"' + (groupObj.disableLeave ? ' disabled' : '') + '><i class="fa fa-times"></i> [[groups:membership.leave-group]]</button>';
|
||||
}
|
||||
|
||||
if (groupObj.isPending && groupObj.name !== 'administrators') {
|
||||
|
||||
@@ -13,6 +13,7 @@ module.exports = function (Groups) {
|
||||
if (data.name === 'administrators') {
|
||||
disableJoinRequests = 1;
|
||||
}
|
||||
const disableLeave = parseInt(data.disableLeave, 10) === 1 ? 1 : 0;
|
||||
const isHidden = parseInt(data.hidden, 10) === 1;
|
||||
|
||||
validateGroupName(data.name);
|
||||
@@ -36,6 +37,7 @@ module.exports = function (Groups) {
|
||||
system: isSystem ? 1 : 0,
|
||||
private: isPrivate,
|
||||
disableJoinRequests: disableJoinRequests,
|
||||
disableLeave: disableLeave,
|
||||
};
|
||||
|
||||
plugins.fireHook('filter:group.create', { group: groupData, data: data });
|
||||
|
||||
@@ -9,7 +9,7 @@ const utils = require('../utils');
|
||||
|
||||
const intFields = [
|
||||
'createtime', 'memberCount', 'hidden', 'system', 'private',
|
||||
'userTitleEnabled', 'disableJoinRequests',
|
||||
'userTitleEnabled', 'disableJoinRequests', 'disableLeave',
|
||||
];
|
||||
|
||||
module.exports = function (Groups) {
|
||||
|
||||
@@ -49,6 +49,10 @@ module.exports = function (Groups) {
|
||||
payload.disableJoinRequests = values.disableJoinRequests ? '1' : '0';
|
||||
}
|
||||
|
||||
if (values.hasOwnProperty('disableLeave')) {
|
||||
payload.disableLeave = values.disableLeave ? '1' : '0';
|
||||
}
|
||||
|
||||
await checkNameChange(groupName, values.name);
|
||||
if (values.hasOwnProperty('private')) {
|
||||
await updatePrivacy(groupName, values.private);
|
||||
|
||||
@@ -43,7 +43,7 @@ SocketGroups.join = async (socket, data) => {
|
||||
});
|
||||
|
||||
if (results.groupData.private && results.groupData.disableJoinRequests) {
|
||||
throw new Error('[[error:join-requests-disabled]]');
|
||||
throw new Error('[[error:group-join-disabled]]');
|
||||
}
|
||||
|
||||
if (!results.groupData.private || results.isAdmin) {
|
||||
@@ -68,6 +68,11 @@ SocketGroups.leave = async (socket, data) => {
|
||||
throw new Error('[[error:cant-remove-self-as-admin]]');
|
||||
}
|
||||
|
||||
const groupData = await groups.getGroupData(data.groupName);
|
||||
if (groupData.disableLeave) {
|
||||
throw new Error('[[error:group-leave-disabled]]');
|
||||
}
|
||||
|
||||
await groups.leave(data.groupName, socket.uid);
|
||||
logGroupEvent(socket, 'group-leave', {
|
||||
groupName: data.groupName,
|
||||
|
||||
@@ -69,7 +69,16 @@
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input id="group-disableJoinRequests" name="disableJoinRequests" type="checkbox"<!-- IF group.disableJoinRequests --> checked<!-- ENDIF group.disableJoinRequests -->>
|
||||
<strong>[[admin/manage/groups:edit.disable-requests]]</strong>
|
||||
<strong>[[admin/manage/groups:edit.disable-join]]</strong>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input id="group-disableLeave" name="disableLeave" type="checkbox"{{{if group.disableLeave}}} checked{{{end}}}>
|
||||
<strong>[[admin/manage/groups:edit.disable-leave]]</strong>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
@@ -38,6 +38,14 @@ describe('Groups', function () {
|
||||
disableJoinRequests: 0,
|
||||
}, next);
|
||||
},
|
||||
async () => {
|
||||
await Groups.create({
|
||||
name: 'PrivateNoLeave',
|
||||
description: 'Private group',
|
||||
private: 1,
|
||||
disableLeave: 1,
|
||||
});
|
||||
},
|
||||
function (next) {
|
||||
// Create a new user
|
||||
User.create({
|
||||
@@ -62,8 +70,8 @@ describe('Groups', function () {
|
||||
},
|
||||
], function (err, results) {
|
||||
assert.ifError(err);
|
||||
testUid = results[3];
|
||||
adminUid = results[4];
|
||||
testUid = results[4];
|
||||
adminUid = results[5];
|
||||
Groups.join('administrators', adminUid, done);
|
||||
});
|
||||
});
|
||||
@@ -72,7 +80,7 @@ describe('Groups', function () {
|
||||
it('should list the groups present', function (done) {
|
||||
Groups.getGroupsFromSet('groups:visible:createtime', 0, -1, function (err, groups) {
|
||||
assert.ifError(err);
|
||||
assert.equal(groups.length, 4);
|
||||
assert.equal(groups.length, 5);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -120,7 +128,7 @@ describe('Groups', function () {
|
||||
it('should return the groups when search query is empty', function (done) {
|
||||
socketGroups.search({ uid: adminUid }, { query: '' }, function (err, groups) {
|
||||
assert.ifError(err);
|
||||
assert.equal(4, groups.length);
|
||||
assert.equal(5, groups.length);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -730,11 +738,21 @@ describe('Groups', function () {
|
||||
it('should fail to join if group is private and join requests are disabled', function (done) {
|
||||
meta.config.allowPrivateGroups = 1;
|
||||
socketGroups.join({ uid: testUid }, { groupName: 'PrivateNoJoin' }, function (err) {
|
||||
assert.equal(err.message, '[[error:join-requests-disabled]]');
|
||||
assert.equal(err.message, '[[error:group-join-disabled]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail to leave if group is private and leave is disabled', async () => {
|
||||
await socketGroups.join({ uid: testUid }, { groupName: 'PrivateNoLeave' });
|
||||
|
||||
try {
|
||||
await socketGroups.leave({ uid: testUid }, { groupName: 'PrivateNoLeave' });
|
||||
} catch (err) {
|
||||
assert.equal(err.message, '[[error:group-leave-disabled]]');
|
||||
}
|
||||
});
|
||||
|
||||
it('should join if user is admin', function (done) {
|
||||
socketGroups.join({ uid: adminUid }, { groupName: 'PrivateCanJoin' }, function (err) {
|
||||
assert.ifError(err);
|
||||
|
||||
Reference in New Issue
Block a user