Skip to content
This repository was archived by the owner on Oct 11, 2022. It is now read-only.

Commit 9115337

Browse files
authored
Merge pull request #2932 from withspectrum/private-channel-mod-fixes
Private channel mod fixes
2 parents 44b0bbe + b5f0f1f commit 9115337

22 files changed

+217
-41
lines changed

api/mutations/channel/disableChannelTokenJoin.js

+18-2
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,28 @@ export default async (
2222
return new UserError('You must be signed in to manage this channel.');
2323
}
2424

25-
const [permissions, settings] = await Promise.all([
25+
const [channelPermissions, channel, settings] = await Promise.all([
2626
loaders.userPermissionsInChannel.load([currentUser.id, channelId]),
27+
loaders.channel.load(channelId),
2728
loaders.channelSettings.load(channelId),
2829
]);
2930

30-
if (!permissions.isOwner) {
31+
const communityPermissions = await loaders.userPermissionsInCommunity.load([
32+
currentUser.id,
33+
channel.communityId,
34+
]);
35+
36+
if (!channelPermissions || !communityPermissions) {
37+
return new UserError("You don't have permission to do this.");
38+
}
39+
40+
const canEdit =
41+
channelPermissions.isOwner ||
42+
channelPermissions.isModerator ||
43+
communityPermissions.isOwner ||
44+
communityPermissions.isModerator;
45+
46+
if (!canEdit) {
3147
return new UserError("You don't have permission to do this.");
3248
}
3349

api/mutations/channel/enableChannelTokenJoin.js

+18-2
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,28 @@ export default async (
2222
return new UserError('You must be signed in to manage this channel.');
2323
}
2424

25-
const [permissions, settings] = await Promise.all([
25+
const [channelPermissions, channel, settings] = await Promise.all([
2626
loaders.userPermissionsInChannel.load([currentUser.id, channelId]),
27+
loaders.channel.load(channelId),
2728
loaders.channelSettings.load(channelId),
2829
]);
2930

30-
if (!permissions.isOwner) {
31+
const communityPermissions = await loaders.userPermissionsInCommunity.load([
32+
currentUser.id,
33+
channel.communityId,
34+
]);
35+
36+
if (!channelPermissions || !communityPermissions) {
37+
return new UserError("You don't have permission to do this.");
38+
}
39+
40+
const canEdit =
41+
channelPermissions.isOwner ||
42+
channelPermissions.isModerator ||
43+
communityPermissions.isOwner ||
44+
communityPermissions.isModerator;
45+
46+
if (!canEdit) {
3147
return new UserError("You don't have permission to do this.");
3248
}
3349

api/mutations/channel/resetChannelJoinToken.js

+18-2
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,28 @@ export default async (
2323
return new UserError('You must be signed in to manage this channel.');
2424
}
2525

26-
const [permissions, settings] = await Promise.all([
26+
const [channelPermissions, channel, settings] = await Promise.all([
2727
loaders.userPermissionsInChannel.load([currentUser.id, channelId]),
28+
loaders.channel.load(channelId),
2829
loaders.channelSettings.load(channelId),
2930
]);
3031

31-
if (!permissions.isOwner) {
32+
const communityPermissions = await loaders.userPermissionsInCommunity.load([
33+
currentUser.id,
34+
channel.communityId,
35+
]);
36+
37+
if (!channelPermissions || !communityPermissions) {
38+
return new UserError("You don't have permission to do this.");
39+
}
40+
41+
const canEdit =
42+
channelPermissions.isOwner ||
43+
channelPermissions.isModerator ||
44+
communityPermissions.isOwner ||
45+
communityPermissions.isModerator;
46+
47+
if (!canEdit) {
3248
return new UserError("You don't have permission to do this.");
3349
}
3450

api/mutations/channel/togglePendingUser.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
getUserPermissionsInCommunity,
1313
createMemberInCommunity,
1414
} from '../../models/usersCommunities';
15+
import { sendPrivateChannelRequestApprovedQueue } from 'shared/bull/queues';
1516

1617
type TogglePendingUserInput = {
1718
input: {
@@ -74,7 +75,9 @@ export default async (
7475
// user is neither a community or channel owner, they don't have permission
7576
if (
7677
currentUserChannelPermissions.isOwner ||
77-
currentUserCommunityPermissions.isOwner
78+
currentUserCommunityPermissions.isOwner ||
79+
currentUserChannelPermissions.isModerator ||
80+
currentUserCommunityPermissions.isModerator
7881
) {
7982
// all checks passed
8083
// determine whether to approve or block them
@@ -91,6 +94,13 @@ export default async (
9194
input.userId
9295
);
9396

97+
sendPrivateChannelRequestApprovedQueue.add({
98+
userId: input.userId,
99+
channelId: input.channelId,
100+
communityId: channelToEvaluate.communityId,
101+
moderatorId: currentUser.id,
102+
});
103+
94104
// if the user is a member of the parent community, we can return
95105
if (evaluatedUserCommunityPermissions.isMember) {
96106
return Promise.all([channelToEvaluate, approveUser]).then(
@@ -113,6 +123,6 @@ export default async (
113123
}
114124

115125
return new UserError(
116-
"You don't have permission to make changes to this channel."
126+
"You don't have permission to manage users in this channel."
117127
);
118128
};

api/mutations/channel/unblockUser.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ export default async (
5959
// if a user owns the community or owns the channel, they can make this change
6060
if (
6161
currentUserChannelPermissions.isOwner ||
62-
currentUserCommunityPermissions.isOwner
62+
currentUserCommunityPermissions.isOwner ||
63+
currentUserChannelPermissions.isModerator ||
64+
currentUserCommunityPermissions.isModerator
6365
) {
6466
return unblockMemberInChannel(input.channelId, input.userId).then(
6567
() => channelToEvaluate

api/mutations/community/disableBrandedLogin.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@ export default async (
2727
loaders.communitySettings.load(communityId),
2828
]);
2929

30-
if (!permissions.isOwner) {
30+
if (!permissions) {
31+
return new UserError("You don't have permission to do this.");
32+
}
33+
34+
const { isOwner, isModerator } = permissions;
35+
36+
if (!isOwner && !isModerator) {
3137
return new UserError("You don't have permission to do this.");
3238
}
3339

api/mutations/community/enableBrandedLogin.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@ export default async (
2727
loaders.communitySettings.load(communityId),
2828
]);
2929

30-
if (!permissions.isOwner) {
30+
if (!permissions) {
31+
return new UserError("You don't have permission to do this.");
32+
}
33+
34+
const { isOwner, isModerator } = permissions;
35+
36+
if (!isOwner && !isModerator) {
3137
return new UserError("You don't have permission to do this.");
3238
}
3339

api/mutations/community/saveBrandedLoginSettings.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,13 @@ export default async (
3434
loaders.communitySettings.load(communityId),
3535
]);
3636

37-
if (!permissions.isOwner) {
37+
if (!permissions) {
38+
return new UserError("You don't have permission to do this.");
39+
}
40+
41+
const { isOwner, isModerator } = permissions;
42+
43+
if (!isOwner && !isModerator) {
3844
return new UserError("You don't have permission to do this.");
3945
}
4046

api/queries/community/billingSettings.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ export default async (
2929
loaders.stripeCustomers.load(stripeCustomerId),
3030
]);
3131

32+
if (!permissions) return defaultResult;
33+
3234
const { isOwner, isModerator } = permissions;
3335
const customer =
3436
stripeCustomer && stripeCustomer.reduction.length > 0
@@ -49,8 +51,9 @@ export default async (
4951
: subscriptions;
5052

5153
return {
52-
pendingAdministratorEmail: isOwner ? pendingAdministratorEmail : null,
53-
administratorEmail: isOwner ? administratorEmail : null,
54+
pendingAdministratorEmail:
55+
isOwner || isModerator ? pendingAdministratorEmail : null,
56+
administratorEmail: isOwner || isModerator ? administratorEmail : null,
5457
sources: sources,
5558
invoices: cleanInvoices,
5659
subscriptions: subscriptions,

api/queries/community/hasChargeableSource.js

+8-4
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@ export default async (
99
) => {
1010
if (!stripeCustomerId || !user) return false;
1111

12-
const {
13-
isOwner,
14-
isModerator,
15-
} = await loaders.userPermissionsInCommunity.load([user.id, id]);
12+
const permissions = await loaders.userPermissionsInCommunity.load([
13+
user.id,
14+
id,
15+
]);
16+
17+
if (!permissions) return null;
18+
19+
const { isOwner, isModerator } = permissions;
1620

1721
if (!isOwner && !isModerator) return null;
1822
return loaders.stripeCustomers.load(stripeCustomerId).then(results => {

athena/models/usersChannels.js

+11
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,14 @@ export const getOwnersInChannel = (
4848
.map(user => user('userId'))
4949
.run();
5050
};
51+
52+
export const getModeratorsInChannel = (
53+
channelId: string
54+
): Promise<Array<string>> => {
55+
return db
56+
.table('usersChannels')
57+
.getAll(channelId, { index: 'channelId' })
58+
.filter({ isModerator: true })
59+
.map(user => user('userId'))
60+
.run();
61+
};

athena/models/usersCommunities.js

+11
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ export const getOwnersInCommunity = (
2323
.then(users => users.map(user => user.userId));
2424
};
2525

26+
export const getModeratorsInCommunity = (
27+
communityId: string
28+
): Promise<Array<string>> => {
29+
return db
30+
.table('usersCommunities')
31+
.getAll(communityId, { index: 'communityId' })
32+
.filter({ isModerator: true })
33+
.run()
34+
.then(users => users.map(user => user.userId));
35+
};
36+
2637
export const getUserPermissionsInCommunity = (
2738
communityId: string,
2839
userId: string

athena/queues/private-channel-request-approved.js

+5-9
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,12 @@ import { getUsers } from '../models/user';
1010
import { fetchPayload } from '../utils/payloads';
1111
import isEmail from 'validator/lib/isEmail';
1212
import { sendPrivateChannelRequestApprovedEmailQueue } from 'shared/bull/queues';
13+
import type {
14+
Job,
15+
PrivateChannelRequestApprovedJobData,
16+
} from 'shared/bull/types';
1317

14-
type JobData = {
15-
data: {
16-
userId: string,
17-
channelId: string,
18-
communityId: string,
19-
moderatorId: string,
20-
},
21-
};
22-
export default async (job: JobData) => {
18+
export default async (job: Job<PrivateChannelRequestApprovedJobData>) => {
2319
const { userId, channelId, communityId, moderatorId } = job.data;
2420
debug(`user request to join channel ${channelId} approved`);
2521

athena/queues/private-channel-request-sent.js

+28-5
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,18 @@ import Raven from 'shared/raven';
66
import { getCommunityById } from '../models/community';
77
import { storeNotification } from '../models/notification';
88
import { storeUsersNotifications } from '../models/usersNotifications';
9-
import { getOwnersInChannel } from '../models/usersChannels';
9+
import {
10+
getOwnersInChannel,
11+
getModeratorsInChannel,
12+
} from '../models/usersChannels';
13+
import {
14+
getOwnersInCommunity,
15+
getModeratorsInCommunity,
16+
} from '../models/usersCommunities';
1017
import { getUsers } from '../models/user';
1118
import { fetchPayload, createPayload } from '../utils/payloads';
1219
import isEmail from 'validator/lib/isEmail';
1320
import { sendPrivateChannelRequestEmailQueue } from 'shared/bull/queues';
14-
import type { DBChannel } from 'shared/types';
1521
import type { Job, PrivateChannelRequestJobData } from 'shared/bull/types';
1622

1723
export default async (job: Job<PrivateChannelRequestJobData>) => {
@@ -45,12 +51,29 @@ export default async (job: Job<PrivateChannelRequestJobData>) => {
4551
const updatedNotification = await storeNotification(nextNotificationRecord);
4652

4753
// get the owners of the channel
48-
const recipients = await getOwnersInChannel(channel.id);
54+
const [
55+
ownersInCommunity,
56+
moderatorsInCommunity,
57+
ownersInChannel,
58+
moderatorsInChannel,
59+
] = await Promise.all([
60+
getOwnersInCommunity(channel.communityId),
61+
getModeratorsInCommunity(channel.communityId),
62+
getOwnersInChannel(channel.id),
63+
getModeratorsInChannel(channel.id),
64+
]);
65+
66+
const uniqueRecipientIds = [
67+
...ownersInCommunity,
68+
...moderatorsInCommunity,
69+
...ownersInChannel,
70+
...moderatorsInChannel,
71+
].filter((item, i, ar) => ar.indexOf(item) === i);
4972

5073
// get all the user data for the owners
51-
const recipientsWithUserData = await getUsers([...recipients]);
74+
const recipientsWithUserData = await getUsers([...uniqueRecipientIds]);
5275

53-
// only get owners with emails
76+
// only get owners + moderators with emails
5477
const filteredRecipients = recipientsWithUserData.filter(
5578
owner => owner.email && isEmail(owner.email)
5679
);

cypress/integration/community_settings_overview_spec.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ describe('Community settings overview tab', () => {
4848
.type(website);
4949
// Submit changes
5050
cy.get('button[type="submit"]').click();
51+
cy.visit(`/${community.slug}`);
5152
cy.location('pathname').should('eq', `/${community.slug}`);
5253
// Make sure changes were applied
5354
cy.contains(description);
@@ -68,13 +69,13 @@ describe('Community settings overview tab', () => {
6869
.clear()
6970
.type(community.website);
7071
cy.get('button[type="submit"]').click();
72+
cy.visit(`/${community.slug}`);
7173
cy.location('pathname').should('eq', `/${community.slug}`);
7274
cy.contains(community.name);
7375
cy.contains(community.description);
7476
cy.contains(community.website);
7577
});
7678

77-
// TODO: FIXME
7879
it.skip('should allow managing branded login settings', () => {
7980
const brandedLoginString = 'Testing branded login custom message';
8081

shared/bull/queues.js

+2
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import {
6363
COMMUNITY_INVOICE_PAID_NOTIFICATION,
6464
REACTION_NOTIFICATION,
6565
PRIVATE_CHANNEL_REQUEST_SENT,
66+
PRIVATE_CHANNEL_REQUEST_APPROVED,
6667
COMMUNITY_INVITE_NOTIFICATION,
6768
CHANNEL_NOTIFICATION,
6869
DIRECT_MESSAGE_NOTIFICATION,
@@ -83,6 +84,7 @@ exports.QUEUE_NAMES = {
8384
sendCommunityInvoicePaidNotificationQueue: COMMUNITY_INVOICE_PAID_NOTIFICATION,
8485
sendReactionNotificationQueue: REACTION_NOTIFICATION,
8586
sendPrivateChannelRequestQueue: PRIVATE_CHANNEL_REQUEST_SENT,
87+
sendPrivateChannelRequestApprovedQueue: PRIVATE_CHANNEL_REQUEST_APPROVED,
8688
sendPrivateChannelInviteNotificationQueue:
8789
'private channel invite notification',
8890
sendCommunityInviteNotificationQueue: COMMUNITY_INVITE_NOTIFICATION,

0 commit comments

Comments
 (0)