Skip to content

Commit 0870d72

Browse files
authored
feat: Replace old voice extension assingment flow (RocketChat#37245)
1 parent 7a7aad5 commit 0870d72

28 files changed

Lines changed: 582 additions & 681 deletions

.changeset/shy-bats-worry.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@rocket.chat/meteor": minor
3+
---
4+
5+
Replaces old `Assign Extension` button and modal by introducing a proper input in the user edit form.

apps/meteor/app/api/server/v1/users.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import { saveCustomFields } from '../../../lib/server/functions/saveCustomFields
5656
import { saveCustomFieldsWithoutValidation } from '../../../lib/server/functions/saveCustomFieldsWithoutValidation';
5757
import { saveUser } from '../../../lib/server/functions/saveUser';
5858
import { sendWelcomeEmail } from '../../../lib/server/functions/saveUser/sendUserEmail';
59+
import { canEditExtension } from '../../../lib/server/functions/saveUser/validateUserEditing';
5960
import { setStatusText } from '../../../lib/server/functions/setStatusText';
6061
import { setUserAvatar } from '../../../lib/server/functions/setUserAvatar';
6162
import { setUsernameWithValidation } from '../../../lib/server/functions/setUsername';
@@ -325,6 +326,10 @@ API.v1.addRoute(
325326
validateCustomFields(this.bodyParams.customFields);
326327
}
327328

329+
if (this.bodyParams.freeSwitchExtension && !(await canEditExtension(this.userId, this.bodyParams.freeSwitchExtension))) {
330+
return API.v1.failure('Setting user voice call extension is not allowed', 'error-action-not-allowed');
331+
}
332+
328333
const newUserId = await saveUser(this.userId, this.bodyParams);
329334
const userId = typeof newUserId !== 'string' ? this.userId : newUserId;
330335

apps/meteor/app/lib/server/functions/saveUser/saveNewUser.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ export const saveNewUser = async function (userData: SaveUserData, sendPassword:
4747
updater.set('emails.0.verified', userData.verified);
4848
}
4949

50+
if (typeof userData.freeSwitchExtension === 'string' && userData.freeSwitchExtension !== '') {
51+
updater.set('freeSwitchExtension', userData.freeSwitchExtension);
52+
}
53+
5054
handleBio(updater, userData.bio);
5155
handleNickname(updater, userData.nickname);
5256

apps/meteor/app/lib/server/functions/saveUser/saveUser.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ export type SaveUserData = {
5353

5454
customFields?: Record<string, any>;
5555
active?: boolean;
56+
57+
freeSwitchExtension?: string;
5658
};
5759
export type UpdateUserData = RequiredField<SaveUserData, '_id'>;
5860
export const isUpdateUserData = (params: SaveUserData): params is UpdateUserData => '_id' in params && !!params._id;
@@ -162,6 +164,14 @@ const _saveUser = (session?: ClientSession) =>
162164
}
163165
}
164166

167+
if (typeof userData.freeSwitchExtension === 'string' && userData.freeSwitchExtension !== (oldUserData?.freeSwitchExtension ?? '')) {
168+
if (userData.freeSwitchExtension.trim() === '') {
169+
updater.unset('freeSwitchExtension');
170+
} else {
171+
updater.set('freeSwitchExtension', userData.freeSwitchExtension);
172+
}
173+
}
174+
165175
if (typeof userData.verified === 'boolean') {
166176
if (oldUserData && 'emails' in oldUserData && oldUserData.emails?.some(({ address }) => address === userData.email)) {
167177
const index = oldUserData.emails.findIndex(({ address }) => address === userData.email);

apps/meteor/app/lib/server/functions/saveUser/validateUserEditing.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable complexity */
12
import { MeteorError } from '@rocket.chat/core-services';
23
import type { IUser } from '@rocket.chat/core-typings';
34
import { Users } from '@rocket.chat/models';
@@ -11,6 +12,22 @@ const isEditingUserRoles = (previousRoles: IUser['roles'], newRoles?: IUser['rol
1112
(newRoles.some((item) => !previousRoles.includes(item)) || previousRoles.some((item) => !newRoles.includes(item)));
1213
const isEditingField = (previousValue?: string, newValue?: string) => typeof newValue !== 'undefined' && newValue !== previousValue;
1314

15+
export const canEditExtension = async (userId: string, newExtension?: string) => {
16+
if (!settings.get('VoIP_TeamCollab_Enabled')) {
17+
return false;
18+
}
19+
20+
if (!(await hasPermissionAsync(userId, 'manage-voip-extensions'))) {
21+
return false;
22+
}
23+
24+
if (newExtension && (await Users.findOneByFreeSwitchExtension(newExtension, { projection: { _id: 1 } }))) {
25+
throw new MeteorError('error-extension-not-available', 'Extension is already assigned to another user');
26+
}
27+
28+
return true;
29+
};
30+
1431
/**
1532
* Validate permissions to edit user fields
1633
*
@@ -97,4 +114,14 @@ export async function validateUserEditing(userId: IUser['_id'], userData: Update
97114
action: 'Update_user',
98115
});
99116
}
117+
118+
if (
119+
isEditingField(user.freeSwitchExtension ?? '', userData.freeSwitchExtension) &&
120+
!(await canEditExtension(userId, userData.freeSwitchExtension))
121+
) {
122+
throw new MeteorError('error-action-not-allowed', 'Edit user voice call extension is not allowed', {
123+
method: 'insertOrUpdateUser',
124+
action: 'Update_user',
125+
});
126+
}
100127
}

apps/meteor/client/components/UserInfo/UserInfo.stories.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ const Template: StoryFn<typeof UserInfo> = (args) => <UserInfo {...defaultArgs}
3535

3636
export const Default = Template.bind({});
3737

38+
export const WithVoiceCallExtension = Template.bind({});
39+
WithVoiceCallExtension.args = {
40+
freeSwitchExtension: '1234567890',
41+
};
42+
3843
export const WithABACAttributes = Template.bind({});
3944
WithABACAttributes.args = {
4045
// @ts-expect-error - abacAttributes is not yet implemented in Users properties

apps/meteor/client/components/UserInfo/UserInfo.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ type UserInfoDataProps = Serialized<
4040
| 'statusText'
4141
| 'canViewAllInfo'
4242
| 'customFields'
43+
| 'freeSwitchExtension'
4344
>
4445
>;
4546

@@ -71,6 +72,7 @@ const UserInfo = ({
7172
canViewAllInfo,
7273
actions,
7374
reason,
75+
freeSwitchExtension,
7476
// @ts-expect-error - abacAttributes is not yet implemented in Users properties
7577
abacAttributes = null,
7678
...props
@@ -180,6 +182,13 @@ const UserInfo = ({
180182
</InfoPanelField>
181183
)}
182184

185+
{freeSwitchExtension && (
186+
<InfoPanelField>
187+
<InfoPanelLabel>{t('Voice_call_extension')}</InfoPanelLabel>
188+
<InfoPanelText>{freeSwitchExtension}</InfoPanelText>
189+
</InfoPanelField>
190+
)}
191+
183192
{abacAttributes?.length > 0 && (
184193
<InfoPanelField>
185194
<InfoPanelLabel title={t('ABAC_Attributes_description')}>{t('ABAC_Attributes')}</InfoPanelLabel>

0 commit comments

Comments
 (0)