feat: option to restrict group leaving, closes #7770

v1.18.x
Julian Lam 6 years ago
parent 4588a4fdcf
commit 1796b65d34

@ -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);

Loading…
Cancel
Save