From 05d5d8e87e12adc88229a7220319021bcc25e658 Mon Sep 17 00:00:00 2001 From: eduardofcabrera <64327259+eduardofcabrera@users.noreply.github.com> Date: Tue, 15 Feb 2022 13:33:52 -0300 Subject: [PATCH 01/37] Convert to typescript the slash commands invite files (#24311) Co-authored-by: Leonardo Ostjen Couto --- app/slashcommands-invite/client/client.js | 7 -- app/slashcommands-invite/client/client.ts | 15 +++ .../client/{index.js => index.ts} | 0 .../server/{index.js => index.ts} | 0 app/slashcommands-invite/server/server.js | 90 ------------------ app/slashcommands-invite/server/server.ts | 91 +++++++++++++++++++ 6 files changed, 106 insertions(+), 97 deletions(-) delete mode 100644 app/slashcommands-invite/client/client.js create mode 100644 app/slashcommands-invite/client/client.ts rename app/slashcommands-invite/client/{index.js => index.ts} (100%) rename app/slashcommands-invite/server/{index.js => index.ts} (100%) delete mode 100644 app/slashcommands-invite/server/server.js create mode 100644 app/slashcommands-invite/server/server.ts diff --git a/app/slashcommands-invite/client/client.js b/app/slashcommands-invite/client/client.js deleted file mode 100644 index f18c8d21c344..000000000000 --- a/app/slashcommands-invite/client/client.js +++ /dev/null @@ -1,7 +0,0 @@ -import { slashCommands } from '../../utils'; - -slashCommands.add('invite', undefined, { - description: 'Invite_user_to_join_channel', - params: '@username', - permission: 'add-user-to-joined-room', -}); diff --git a/app/slashcommands-invite/client/client.ts b/app/slashcommands-invite/client/client.ts new file mode 100644 index 000000000000..4a494287d0ae --- /dev/null +++ b/app/slashcommands-invite/client/client.ts @@ -0,0 +1,15 @@ +import { slashCommands } from '../../utils/lib/slashCommand'; + +slashCommands.add( + 'invite', + undefined, + { + description: 'Invite_user_to_join_channel', + params: '@username', + permission: 'add-user-to-joined-room', + }, + undefined, + false, + undefined, + undefined, +); diff --git a/app/slashcommands-invite/client/index.js b/app/slashcommands-invite/client/index.ts similarity index 100% rename from app/slashcommands-invite/client/index.js rename to app/slashcommands-invite/client/index.ts diff --git a/app/slashcommands-invite/server/index.js b/app/slashcommands-invite/server/index.ts similarity index 100% rename from app/slashcommands-invite/server/index.js rename to app/slashcommands-invite/server/index.ts diff --git a/app/slashcommands-invite/server/server.js b/app/slashcommands-invite/server/server.js deleted file mode 100644 index eca11742a34f..000000000000 --- a/app/slashcommands-invite/server/server.js +++ /dev/null @@ -1,90 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; -import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; - -import { slashCommands } from '../../utils'; -import { Subscriptions } from '../../models'; -import { api } from '../../../server/sdk/api'; - -/* - * Invite is a named function that will replace /invite commands - * @param {Object} message - The message object - */ - -function Invite(command, params, item) { - if (command !== 'invite' || !Match.test(params, String)) { - return; - } - - const usernames = params - .split(/[\s,]/) - .map((username) => username.replace(/(^@)|( @)/, '')) - .filter((a) => a !== ''); - if (usernames.length === 0) { - return; - } - let users = Meteor.users.find({ - username: { - $in: usernames, - }, - }); - const userId = Meteor.userId(); - const currentUser = Meteor.users.findOne(userId); - if (users.count() === 0) { - api.broadcast('notify.ephemeralMessage', userId, item.rid, { - msg: TAPi18n.__( - 'User_doesnt_exist', - { - postProcess: 'sprintf', - sprintf: [usernames.join(' @')], - }, - currentUser.language, - ), - }); - return; - } - users = users.fetch().filter(function (user) { - const subscription = Subscriptions.findOneByRoomIdAndUserId(item.rid, user._id, { - fields: { _id: 1 }, - }); - if (subscription == null) { - return true; - } - api.broadcast('notify.ephemeralMessage', userId, item.rid, { - msg: TAPi18n.__( - 'Username_is_already_in_here', - { - postProcess: 'sprintf', - sprintf: [user.username], - }, - currentUser.language, - ), - }); - return false; - }); - - users.forEach(function (user) { - try { - return Meteor.call('addUserToRoom', { - rid: item.rid, - username: user.username, - }); - } catch ({ error }) { - if (error === 'cant-invite-for-direct-room') { - api.broadcast('notify.ephemeralMessage', userId, item.rid, { - msg: TAPi18n.__('Cannot_invite_users_to_direct_rooms', null, currentUser.language), - }); - } else { - api.broadcast('notify.ephemeralMessage', userId, item.rid, { - msg: TAPi18n.__(error, null, currentUser.language), - }); - } - } - }); -} - -slashCommands.add('invite', Invite, { - description: 'Invite_user_to_join_channel', - params: '@username', - permission: 'add-user-to-joined-room', -}); diff --git a/app/slashcommands-invite/server/server.ts b/app/slashcommands-invite/server/server.ts new file mode 100644 index 000000000000..afe867553602 --- /dev/null +++ b/app/slashcommands-invite/server/server.ts @@ -0,0 +1,91 @@ +import { Meteor } from 'meteor/meteor'; +import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; + +import { settings } from '../../settings/server'; +import { slashCommands } from '../../utils/lib/slashCommand'; +import { Subscriptions } from '../../models/server'; +import { api } from '../../../server/sdk/api'; + +/* + * Invite is a named function that will replace /invite commands + * @param {Object} message - The message object + */ + +function Invite(_command: 'invite', params: string, item: Record): void { + const usernames = params + .split(/[\s,]/) + .map((username) => username.replace(/(^@)|( @)/, '')) + .filter((a) => a !== ''); + if (usernames.length === 0) { + return; + } + const users = Meteor.users.find({ + username: { + $in: usernames, + }, + }); + const userId = Meteor.userId() as string; + if (users.count() === 0) { + api.broadcast('notify.ephemeralMessage', userId, item.rid, { + msg: TAPi18n.__('User_doesnt_exist', { + postProcess: 'sprintf', + sprintf: [usernames.join(' @')], + lng: settings.get('Language') || 'en', + }), + }); + return; + } + const usersFiltered = users.fetch().filter(function (user) { + const subscription = Subscriptions.findOneByRoomIdAndUserId(item.rid, user._id, { + fields: { _id: 1 }, + }); + if (subscription == null) { + return true; + } + const usernameStr = user.username as string; + api.broadcast('notify.ephemeralMessage', userId, item.rid, { + msg: TAPi18n.__('Username_is_already_in_here', { + postProcess: 'sprintf', + sprintf: [usernameStr], + lng: settings.get('Language') || 'en', + }), + }); + return false; + }); + + usersFiltered.forEach(function (user) { + try { + return Meteor.call('addUserToRoom', { + rid: item.rid, + username: user.username, + }); + } catch ({ error }) { + if (typeof error !== 'string') { + return; + } + if (error === 'cant-invite-for-direct-room') { + api.broadcast('notify.ephemeralMessage', userId, item.rid, { + msg: TAPi18n.__('Cannot_invite_users_to_direct_rooms', { lng: settings.get('Language') || 'en' }), + }); + } else { + api.broadcast('notify.ephemeralMessage', userId, item.rid, { + msg: TAPi18n.__(error, { lng: settings.get('Language') || 'en' }), + }); + } + } + }); +} + +slashCommands.add( + 'invite', + Invite, + { + description: 'Invite_user_to_join_channel', + params: '@username', + permission: 'add-user-to-joined-room', + }, + undefined, + false, + undefined, + undefined, +); From 7d60960b9a8851dc983dbf7978afc77f697e90aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Feb 2022 14:20:59 -0300 Subject: [PATCH 02/37] Bump sodium-native from 3.2.1 to 3.3.0 in /ee/server/services (#23512) Bumps [sodium-native](https://github.com/sodium-friends/sodium-native) from 3.2.1 to 3.3.0. - [Release notes](https://github.com/sodium-friends/sodium-native/releases) - [Changelog](https://github.com/sodium-friends/sodium-native/blob/master/CHANGELOG.md) - [Commits](https://github.com/sodium-friends/sodium-native/compare/v3.2.1...v3.3.0) --- updated-dependencies: - dependency-name: sodium-native dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ee/server/services/package-lock.json | 18 +++++++++--------- ee/server/services/package.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ee/server/services/package-lock.json b/ee/server/services/package-lock.json index cea4078143b9..5ce73bc19368 100644 --- a/ee/server/services/package-lock.json +++ b/ee/server/services/package-lock.json @@ -1623,7 +1623,8 @@ "ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true }, "ip": { "version": "1.1.5", @@ -2110,9 +2111,9 @@ } }, "node-gyp-build": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz", - "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==" + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==" }, "node-int64": { "version": "0.4.0", @@ -2878,12 +2879,11 @@ } }, "sodium-native": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-3.2.1.tgz", - "integrity": "sha512-EgDZ/Z7PxL2kCasKk7wnRkV8W9kvwuIlHuHXAxkQm3FF0MgVsjyLBXGjSRGhjE6u7rhSpk3KaMfFM23bfMysIQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-3.3.0.tgz", + "integrity": "sha512-rg6lCDM/qa3p07YGqaVD+ciAbUqm6SoO4xmlcfkbU5r1zIGrguXztLiEtaLYTV5U6k8KSIUFmnU3yQUSKmf6DA==", "requires": { - "ini": "^1.3.5", - "node-gyp-build": "^4.2.0" + "node-gyp-build": "^4.3.0" } }, "sodium-plus": { diff --git a/ee/server/services/package.json b/ee/server/services/package.json index 1a74368cdfd1..38441068f341 100644 --- a/ee/server/services/package.json +++ b/ee/server/services/package.json @@ -42,7 +42,7 @@ "mongodb": "^3.6.10", "nats": "^2.4.0", "pino": "^7.6.4", - "sodium-native": "^3.2.1", + "sodium-native": "^3.3.0", "sodium-plus": "^0.9.0", "underscore.string": "^3.3.6", "uuid": "^7.0.3", From b58012bdcb977d07b6084e09fa3d7c4287664425 Mon Sep 17 00:00:00 2001 From: eduardofcabrera <64327259+eduardofcabrera@users.noreply.github.com> Date: Tue, 15 Feb 2022 15:37:05 -0300 Subject: [PATCH 03/37] Convert to typescript the me slashCommands files (#24321) Co-authored-by: Leonardo Ostjen Couto --- app/slashcommands-me/{index.js => index.ts} | 0 app/slashcommands-me/server/{index.js => index.ts} | 0 app/slashcommands-me/server/{me.js => me.ts} | 12 ++++++------ 3 files changed, 6 insertions(+), 6 deletions(-) rename app/slashcommands-me/{index.js => index.ts} (100%) rename app/slashcommands-me/server/{index.js => index.ts} (100%) rename app/slashcommands-me/server/{me.js => me.ts} (68%) diff --git a/app/slashcommands-me/index.js b/app/slashcommands-me/index.ts similarity index 100% rename from app/slashcommands-me/index.js rename to app/slashcommands-me/index.ts diff --git a/app/slashcommands-me/server/index.js b/app/slashcommands-me/server/index.ts similarity index 100% rename from app/slashcommands-me/server/index.js rename to app/slashcommands-me/server/index.ts diff --git a/app/slashcommands-me/server/me.js b/app/slashcommands-me/server/me.ts similarity index 68% rename from app/slashcommands-me/server/me.js rename to app/slashcommands-me/server/me.ts index 1de3d5f7d0fc..ff536c475460 100644 --- a/app/slashcommands-me/server/me.js +++ b/app/slashcommands-me/server/me.ts @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import s from 'underscore.string'; -import { slashCommands } from '../../utils'; +import { slashCommands } from '../../utils/lib/slashCommand'; /* * Me is a named function that will replace /me commands @@ -9,11 +9,7 @@ import { slashCommands } from '../../utils'; */ slashCommands.add( 'me', - function Me(command, params, item) { - if (command !== 'me') { - return; - } - + function Me(_command: 'me', params: string, item: Record): void { if (s.trim(params)) { const msg = item; msg.msg = `_${params}_`; @@ -24,4 +20,8 @@ slashCommands.add( description: 'Displays_action_text', params: 'your_message', }, + undefined, + false, + undefined, + undefined, ); From 4487f9ecc0fcfb0b11eb327e775618d190d7c246 Mon Sep 17 00:00:00 2001 From: eduardofcabrera <64327259+eduardofcabrera@users.noreply.github.com> Date: Tue, 15 Feb 2022 18:05:17 -0300 Subject: [PATCH 04/37] Chore: Convert to typescript the mute and unmute slash commands files (#24325) * Convert to typescript the mute and unmute slash commands files * Lint fix Co-authored-by: Leonardo Ostjen Couto --- app/slashcommands-mute/{index.js => index.ts} | 0 .../server/{index.js => index.ts} | 0 app/slashcommands-mute/server/mute.js | 66 ------------------- app/slashcommands-mute/server/mute.ts | 61 +++++++++++++++++ app/slashcommands-mute/server/unmute.js | 63 ------------------ app/slashcommands-mute/server/unmute.ts | 60 +++++++++++++++++ 6 files changed, 121 insertions(+), 129 deletions(-) rename app/slashcommands-mute/{index.js => index.ts} (100%) rename app/slashcommands-mute/server/{index.js => index.ts} (100%) delete mode 100644 app/slashcommands-mute/server/mute.js create mode 100644 app/slashcommands-mute/server/mute.ts delete mode 100644 app/slashcommands-mute/server/unmute.js create mode 100644 app/slashcommands-mute/server/unmute.ts diff --git a/app/slashcommands-mute/index.js b/app/slashcommands-mute/index.ts similarity index 100% rename from app/slashcommands-mute/index.js rename to app/slashcommands-mute/index.ts diff --git a/app/slashcommands-mute/server/index.js b/app/slashcommands-mute/server/index.ts similarity index 100% rename from app/slashcommands-mute/server/index.js rename to app/slashcommands-mute/server/index.ts diff --git a/app/slashcommands-mute/server/mute.js b/app/slashcommands-mute/server/mute.js deleted file mode 100644 index 6858cc07a563..000000000000 --- a/app/slashcommands-mute/server/mute.js +++ /dev/null @@ -1,66 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; -import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; - -import { slashCommands } from '../../utils'; -import { Users, Subscriptions } from '../../models'; -import { api } from '../../../server/sdk/api'; - -/* - * Mute is a named function that will replace /mute commands - */ - -slashCommands.add( - 'mute', - function Mute(command, params, item) { - if (command !== 'mute' || !Match.test(params, String)) { - return; - } - const username = params.trim().replace('@', ''); - if (username === '') { - return; - } - const userId = Meteor.userId(); - const user = Meteor.users.findOne(userId); - const mutedUser = Users.findOneByUsernameIgnoringCase(username); - if (mutedUser == null) { - api.broadcast('notify.ephemeralMessage', userId, item.rid, { - msg: TAPi18n.__( - 'Username_doesnt_exist', - { - postProcess: 'sprintf', - sprintf: [username], - }, - user.language, - ), - }); - return; - } - - const subscription = Subscriptions.findOneByRoomIdAndUserId(item.rid, mutedUser._id, { - fields: { _id: 1 }, - }); - if (!subscription) { - api.broadcast('notify.ephemeralMessage', userId, item.rid, { - msg: TAPi18n.__( - 'Username_is_not_in_this_room', - { - postProcess: 'sprintf', - sprintf: [username], - }, - user.language, - ), - }); - return; - } - Meteor.call('muteUserInRoom', { - rid: item.rid, - username, - }); - }, - { - description: 'Mute_someone_in_room', - params: '@username', - permission: 'mute-user', - }, -); diff --git a/app/slashcommands-mute/server/mute.ts b/app/slashcommands-mute/server/mute.ts new file mode 100644 index 000000000000..ee000c5667f3 --- /dev/null +++ b/app/slashcommands-mute/server/mute.ts @@ -0,0 +1,61 @@ +import { Meteor } from 'meteor/meteor'; +import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; + +import { slashCommands } from '../../utils/lib/slashCommand'; +import { settings } from '../../settings/server'; +import { Users, Subscriptions } from '../../models/server'; +import { api } from '../../../server/sdk/api'; + +/* + * Mute is a named function that will replace /mute commands + */ + +function Mute(_command: 'mute', params: string, item: Record): void { + const username = params.trim().replace('@', ''); + if (username === '') { + return; + } + + const userId = Meteor.userId() as string; + const mutedUser = Users.findOneByUsernameIgnoringCase(username); + if (mutedUser == null) { + api.broadcast('notify.ephemeralMessage', userId, item.rid, { + msg: TAPi18n.__('Username_doesnt_exist', { + postProcess: 'sprintf', + sprintf: [username], + lng: settings.get('Language') || 'en', + }), + }); + } + const subscription = Subscriptions.findOneByRoomIdAndUserId(item.rid, mutedUser._id, { + fields: { _id: 1 }, + }); + if (!subscription) { + api.broadcast('notify.ephemeralMessage', userId, item.rid, { + msg: TAPi18n.__('Username_is_not_in_this_room', { + postProcess: 'sprintf', + sprintf: [username], + lng: settings.get('Language') || 'en', + }), + }); + return; + } + Meteor.call('muteUserInRoom', { + rid: item.rid, + username, + }); +} + +slashCommands.add( + 'mute', + Mute, + { + description: 'Mute_someone_in_room', + params: '@username', + permission: 'mute-user', + }, + undefined, + false, + undefined, + undefined, +); diff --git a/app/slashcommands-mute/server/unmute.js b/app/slashcommands-mute/server/unmute.js deleted file mode 100644 index 4d9a51857324..000000000000 --- a/app/slashcommands-mute/server/unmute.js +++ /dev/null @@ -1,63 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; -import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; - -import { slashCommands } from '../../utils'; -import { Users, Subscriptions } from '../../models'; -import { api } from '../../../server/sdk/api'; - -/* - * Unmute is a named function that will replace /unmute commands - */ - -slashCommands.add( - 'unmute', - function Unmute(command, params, item) { - if (command !== 'unmute' || !Match.test(params, String)) { - return; - } - const username = params.trim().replace('@', ''); - if (username === '') { - return; - } - const user = Meteor.users.findOne(Meteor.userId()); - const unmutedUser = Users.findOneByUsernameIgnoringCase(username); - if (unmutedUser == null) { - return api.broadcast('notify.ephemeralMessage', Meteor.userId(), item.rid, { - msg: TAPi18n.__( - 'Username_doesnt_exist', - { - postProcess: 'sprintf', - sprintf: [username], - }, - user.language, - ), - }); - } - - const subscription = Subscriptions.findOneByRoomIdAndUserId(item.rid, unmutedUser._id, { - fields: { _id: 1 }, - }); - if (!subscription) { - return api.broadcast('notify.ephemeralMessage', Meteor.userId(), item.rid, { - msg: TAPi18n.__( - 'Username_is_not_in_this_room', - { - postProcess: 'sprintf', - sprintf: [username], - }, - user.language, - ), - }); - } - Meteor.call('unmuteUserInRoom', { - rid: item.rid, - username, - }); - }, - { - description: 'Unmute_someone_in_room', - params: '@username', - permission: 'mute-user', - }, -); diff --git a/app/slashcommands-mute/server/unmute.ts b/app/slashcommands-mute/server/unmute.ts new file mode 100644 index 000000000000..bcc43f1b1f10 --- /dev/null +++ b/app/slashcommands-mute/server/unmute.ts @@ -0,0 +1,60 @@ +import { Meteor } from 'meteor/meteor'; +import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; + +import { slashCommands } from '../../utils/lib/slashCommand'; +import { Users, Subscriptions } from '../../models/server'; +import { settings } from '../../settings/server'; +import { api } from '../../../server/sdk/api'; + +/* + * Unmute is a named function that will replace /unmute commands + */ + +function Unmute(_command: 'unmute', params: string, item: Record): void | Promise { + const username = params.trim().replace('@', ''); + if (username === '') { + return; + } + const userId = Meteor.userId() as string; + const unmutedUser = Users.findOneByUsernameIgnoringCase(username); + if (unmutedUser == null) { + return api.broadcast('notify.ephemeralMessage', userId, item.rid, { + msg: TAPi18n.__('Username_doesnt_exist', { + postProcess: 'sprintf', + sprintf: [username], + lng: settings.get('Language') || 'en', + }), + }); + } + + const subscription = Subscriptions.findOneByRoomIdAndUserId(item.rid, unmutedUser._id, { + fields: { _id: 1 }, + }); + if (!subscription) { + return api.broadcast('notify.ephemeralMessage', userId, item.rid, { + msg: TAPi18n.__('Username_is_not_in_this_room', { + postProcess: 'sprintf', + sprintf: [username], + lng: settings.get('Language') || 'en', + }), + }); + } + Meteor.call('unmuteUserInRoom', { + rid: item.rid, + username, + }); +} + +slashCommands.add( + 'unmute', + Unmute, + { + description: 'Unmute_someone_in_room', + params: '@username', + permission: 'mute-user', + }, + undefined, + false, + undefined, + undefined, +); From 61fa8f140f99e2c9652a8d3bb314aaa2264415ea Mon Sep 17 00:00:00 2001 From: eduardofcabrera <64327259+eduardofcabrera@users.noreply.github.com> Date: Tue, 15 Feb 2022 19:06:20 -0300 Subject: [PATCH 05/37] Convert to typescript the slash commands create files (#24306) Co-authored-by: Leonardo Ostjen Couto --- app/slashcommands-create/client/client.js | 7 -- app/slashcommands-create/client/client.ts | 15 ++++ .../client/{index.js => index.ts} | 0 .../server/{index.js => index.ts} | 0 app/slashcommands-create/server/server.js | 62 ----------------- app/slashcommands-create/server/server.ts | 68 +++++++++++++++++++ 6 files changed, 83 insertions(+), 69 deletions(-) delete mode 100644 app/slashcommands-create/client/client.js create mode 100644 app/slashcommands-create/client/client.ts rename app/slashcommands-create/client/{index.js => index.ts} (100%) rename app/slashcommands-create/server/{index.js => index.ts} (100%) delete mode 100644 app/slashcommands-create/server/server.js create mode 100644 app/slashcommands-create/server/server.ts diff --git a/app/slashcommands-create/client/client.js b/app/slashcommands-create/client/client.js deleted file mode 100644 index 68a96f6ae3a8..000000000000 --- a/app/slashcommands-create/client/client.js +++ /dev/null @@ -1,7 +0,0 @@ -import { slashCommands } from '../../utils'; - -slashCommands.add('create', null, { - description: 'Create_A_New_Channel', - params: '#channel', - permission: ['create-c', 'create-p'], -}); diff --git a/app/slashcommands-create/client/client.ts b/app/slashcommands-create/client/client.ts new file mode 100644 index 000000000000..f618d472f096 --- /dev/null +++ b/app/slashcommands-create/client/client.ts @@ -0,0 +1,15 @@ +import { slashCommands } from '../../utils/lib/slashCommand'; + +slashCommands.add( + 'create', + undefined, + { + description: 'Create_A_New_Channel', + params: '#channel', + permission: ['create-c', 'create-p'], + }, + undefined, + false, + undefined, + undefined, +); diff --git a/app/slashcommands-create/client/index.js b/app/slashcommands-create/client/index.ts similarity index 100% rename from app/slashcommands-create/client/index.js rename to app/slashcommands-create/client/index.ts diff --git a/app/slashcommands-create/server/index.js b/app/slashcommands-create/server/index.ts similarity index 100% rename from app/slashcommands-create/server/index.js rename to app/slashcommands-create/server/index.ts diff --git a/app/slashcommands-create/server/server.js b/app/slashcommands-create/server/server.js deleted file mode 100644 index 14b6b6d3867b..000000000000 --- a/app/slashcommands-create/server/server.js +++ /dev/null @@ -1,62 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; -import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; - -import { settings } from '../../settings'; -import { Rooms } from '../../models'; -import { slashCommands } from '../../utils'; -import { api } from '../../../server/sdk/api'; - -function Create(command, params, item) { - function getParams(str) { - const regex = /(--(\w+))+/g; - const result = []; - let m; - while ((m = regex.exec(str)) !== null) { - if (m.index === regex.lastIndex) { - regex.lastIndex++; - } - result.push(m[2]); - } - return result; - } - - const regexp = new RegExp(settings.get('UTF8_Channel_Names_Validation')); - - if (command !== 'create' || !Match.test(params, String)) { - return; - } - let channel = regexp.exec(params.trim()); - channel = channel ? channel[0] : ''; - if (channel === '') { - return; - } - - const user = Meteor.users.findOne(Meteor.userId()); - const room = Rooms.findOneByName(channel); - if (room != null) { - api.broadcast('notify.ephemeralMessage', Meteor.userId(), item.rid, { - msg: TAPi18n.__( - 'Channel_already_exist', - { - postProcess: 'sprintf', - sprintf: [channel], - }, - user.language, - ), - }); - return; - } - - if (getParams(params).indexOf('private') > -1) { - return Meteor.call('createPrivateGroup', channel, []); - } - - Meteor.call('createChannel', channel, []); -} - -slashCommands.add('create', Create, { - description: 'Create_A_New_Channel', - params: '#channel', - permission: ['create-c', 'create-p'], -}); diff --git a/app/slashcommands-create/server/server.ts b/app/slashcommands-create/server/server.ts new file mode 100644 index 000000000000..ada7ff233883 --- /dev/null +++ b/app/slashcommands-create/server/server.ts @@ -0,0 +1,68 @@ +import { Meteor } from 'meteor/meteor'; +import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; + +import { settings } from '../../settings/server'; +import { Rooms } from '../../models/server'; +import { slashCommands } from '../../utils/lib/slashCommand'; +import { api } from '../../../server/sdk/api'; + +function Create(_command: 'create', params: string, item: Record): void { + function getParams(str: string): string[] { + const regex = /(--(\w+))+/g; + const result = []; + let m; + while ((m = regex.exec(str)) !== null) { + if (m.index === regex.lastIndex) { + regex.lastIndex++; + } + result.push(m[2]); + } + return result; + } + + const regexp = new RegExp(settings.get('UTF8_Channel_Names_Validation') as string); + + const channel = regexp.exec(params.trim()); + + if (!channel) { + return; + } + + const channelStr: string = channel ? channel[0] : ''; + if (channelStr === '') { + return; + } + const userId = Meteor.userId() as string; + + const room = Rooms.findOneByName(channelStr); + if (room != null) { + api.broadcast('notify.ephemeralMessage', userId, item.rid, { + msg: TAPi18n.__('Channel_already_exist', { + postProcess: 'sprintf', + sprintf: [channelStr], + lng: settings.get('Language') || 'en', + }), + }); + return; + } + + if (getParams(params).indexOf('private') > -1) { + return Meteor.call('createPrivateGroup', channelStr, []); + } + + Meteor.call('createChannel', channelStr, []); +} + +slashCommands.add( + 'create', + Create, + { + description: 'Create_A_New_Channel', + params: '#channel', + permission: ['create-c', 'create-p'], + }, + undefined, + false, + undefined, + undefined, +); From c83a78f5e658186059a130fd31ec2945e5053437 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Tue, 15 Feb 2022 22:24:38 -0600 Subject: [PATCH 06/37] [FIX] GDPR action to forget visitor data on request (#24441) Co-authored-by: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com> --- app/livechat/server/api/v1/visitor.ts | 4 +++- app/livechat/server/config.ts | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/livechat/server/api/v1/visitor.ts b/app/livechat/server/api/v1/visitor.ts index 7f944e47a350..5671c84bcacd 100644 --- a/app/livechat/server/api/v1/visitor.ts +++ b/app/livechat/server/api/v1/visitor.ts @@ -8,6 +8,7 @@ import { findGuest, normalizeHttpHeaderData } from '../lib/livechat'; import { Livechat } from '../../lib/Livechat'; import { ILivechatVisitorDTO } from '../../../../../definition/ILivechatVisitor'; import { IRoom } from '../../../../../definition/IRoom'; +import { settings } from '../../../../settings/server'; API.v1.addRoute('livechat/visitor', { async post() { @@ -104,7 +105,8 @@ API.v1.addRoute('livechat/visitor/:token', { }, }).fetch(); - if (rooms?.length) { + // if gdpr is enabled, bypass rooms check + if (rooms?.length && !settings.get('Livechat_Allow_collect_and_store_HTTP_header_informations')) { throw new Meteor.Error('visitor-has-open-rooms', 'Cannot remove visitors with opened rooms'); } diff --git a/app/livechat/server/config.ts b/app/livechat/server/config.ts index 2b1ed7b6f6d5..a8b2fae1825f 100644 --- a/app/livechat/server/config.ts +++ b/app/livechat/server/config.ts @@ -567,7 +567,7 @@ Meteor.startup(function () { alert: 'Force_visitor_to_accept_data_processing_consent_enabled_alert', i18nLabel: 'Force_visitor_to_accept_data_processing_consent', i18nDescription: 'Force_visitor_to_accept_data_processing_consent_description', - enableQuery: omnichannelEnabledQuery, + enableQuery: [omnichannelEnabledQuery, { _id: 'Livechat_Allow_collect_and_store_HTTP_header_informations', value: true }], }); this.add('Livechat_data_processing_consent_text', '', { @@ -578,7 +578,11 @@ Meteor.startup(function () { public: true, i18nLabel: 'Data_processing_consent_text', i18nDescription: 'Data_processing_consent_text_description', - enableQuery: [{ _id: 'Livechat_force_accept_data_processing_consent', value: true }, omnichannelEnabledQuery], + enableQuery: [ + { _id: 'Livechat_force_accept_data_processing_consent', value: true }, + { _id: 'Livechat_Allow_collect_and_store_HTTP_header_informations', value: true }, + omnichannelEnabledQuery, + ], }); this.add('Livechat_agent_leave_action', 'none', { From c58dd0e522c0846ca13cdbf1497da7abf4790a78 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Feb 2022 09:46:01 -0300 Subject: [PATCH 07/37] Bump body-parser from 1.19.1 to 1.19.2 in /ee/server/services (#24517) Bumps [body-parser](https://github.com/expressjs/body-parser) from 1.19.1 to 1.19.2. - [Release notes](https://github.com/expressjs/body-parser/releases) - [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md) - [Commits](https://github.com/expressjs/body-parser/compare/1.19.1...1.19.2) --- updated-dependencies: - dependency-name: body-parser dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ee/server/services/package-lock.json | 77 ++++++++++++++++------------ ee/server/services/package.json | 2 +- 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/ee/server/services/package-lock.json b/ee/server/services/package-lock.json index 5ce73bc19368..12890caf38e2 100644 --- a/ee/server/services/package-lock.json +++ b/ee/server/services/package-lock.json @@ -740,19 +740,19 @@ "dev": true }, "body-parser": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", - "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", "requires": { - "bytes": "3.1.1", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", "depd": "~1.1.2", "http-errors": "1.8.1", "iconv-lite": "0.4.24", "on-finished": "~2.3.0", - "qs": "6.9.6", - "raw-body": "2.4.2", + "qs": "6.9.7", + "raw-body": "2.4.3", "type-is": "~1.6.18" }, "dependencies": { @@ -764,37 +764,26 @@ "ms": "2.0.0" } }, - "http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - } - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==" }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + "raw-body": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", + "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "requires": { + "bytes": "3.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } } } }, @@ -848,9 +837,9 @@ } }, "bytes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", - "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, "camelcase": { "version": "5.0.0", @@ -1261,6 +1250,28 @@ "vary": "~1.1.2" }, "dependencies": { + "body-parser": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", + "requires": { + "bytes": "3.1.1", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" + } + }, + "bytes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==" + }, "cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", diff --git a/ee/server/services/package.json b/ee/server/services/package.json index 38441068f341..29f842aff859 100644 --- a/ee/server/services/package.json +++ b/ee/server/services/package.json @@ -28,7 +28,7 @@ "@rocket.chat/ui-kit": "^0.31.2", "ajv": "^8.7.1", "bcrypt": "^5.0.1", - "body-parser": "^1.19.1", + "body-parser": "^1.19.2", "colorette": "^1.3.0", "cookie": "^0.4.2", "cookie-parser": "^1.4.6", From d02ea1daf711b6d32c6357dd77a91e37b543f42d Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Wed, 16 Feb 2022 12:56:45 -0300 Subject: [PATCH 08/37] Chore: `twoFactorRequired` signature (#24518) --- app/2fa/server/twoFactorRequired.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/2fa/server/twoFactorRequired.ts b/app/2fa/server/twoFactorRequired.ts index 0fb1e8cd9d1f..1cda35f5bcca 100644 --- a/app/2fa/server/twoFactorRequired.ts +++ b/app/2fa/server/twoFactorRequired.ts @@ -1,10 +1,12 @@ import { Meteor } from 'meteor/meteor'; import { checkCodeForUser, ITwoFactorOptions } from './code/index'; -import { IMethodThisType } from '../../../definition/IMethodThisType'; -export function twoFactorRequired(fn: Function, options: ITwoFactorOptions): Function { - return function (this: IMethodThisType, ...args: any[]): any { +export function twoFactorRequired any>( + fn: TFunction, + options?: ITwoFactorOptions, +): (this: Meteor.MethodThisType, ...args: Parameters) => ReturnType { + return function (this: Meteor.MethodThisType, ...args: Parameters): ReturnType { if (!this.userId) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'twoFactorRequired' }); } From cbca495ccb6fbfbd888ba0fad14a3645c35ef492 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 08:24:08 -0300 Subject: [PATCH 09/37] Bump express from 4.17.2 to 4.17.3 in /ee/server/services (#24522) Bumps [express](https://github.com/expressjs/express) from 4.17.2 to 4.17.3. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.17.2...4.17.3) --- updated-dependencies: - dependency-name: express dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ee/server/services/package-lock.json | 58 +++++++++------------------- ee/server/services/package.json | 2 +- 2 files changed, 19 insertions(+), 41 deletions(-) diff --git a/ee/server/services/package-lock.json b/ee/server/services/package-lock.json index 12890caf38e2..fe1903a81b9c 100644 --- a/ee/server/services/package-lock.json +++ b/ee/server/services/package-lock.json @@ -1214,16 +1214,16 @@ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, "express": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", - "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", + "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", "requires": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.1", + "body-parser": "1.19.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.1", + "cookie": "0.4.2", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "~1.1.2", @@ -1238,7 +1238,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.9.6", + "qs": "6.9.7", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.17.2", @@ -1250,33 +1250,6 @@ "vary": "~1.1.2" }, "dependencies": { - "body-parser": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", - "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", - "requires": { - "bytes": "3.1.1", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.6", - "raw-body": "2.4.2", - "type-is": "~1.6.18" - } - }, - "bytes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", - "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==" - }, - "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -2569,9 +2542,9 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==" }, "quick-format-unescaped": { "version": "4.0.4", @@ -2587,6 +2560,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", + "dev": true, "requires": { "bytes": "3.1.1", "http-errors": "1.8.1", @@ -2597,12 +2571,14 @@ "bytes": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", - "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==" + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", + "dev": true }, "http-errors": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, "requires": { "depd": "~1.1.2", "inherits": "2.0.4", @@ -2614,12 +2590,14 @@ "setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true } } }, diff --git a/ee/server/services/package.json b/ee/server/services/package.json index 29f842aff859..e6f452bed5f5 100644 --- a/ee/server/services/package.json +++ b/ee/server/services/package.json @@ -34,7 +34,7 @@ "cookie-parser": "^1.4.6", "ejson": "^2.2.2", "eventemitter3": "^4.0.7", - "express": "^4.17.2", + "express": "^4.17.3", "fibers": "^5.0.1", "jaeger-client": "^3.19.0", "mem": "^8.1.1", From 26aa1bdd85461fee7168b6bb929839d6e0bcbbe4 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Thu, 17 Feb 2022 22:07:21 +0530 Subject: [PATCH 10/37] [IMPROVE] Descriptive tooltip for Encrypted Key on Room Header (#24121) * add tooltip with permission check * remove unnecessary line space * permission role and migration * requested changes * missing import --- app/authorization/server/functions/upsertPermissions.ts | 2 +- client/views/room/Header/icons/Encrypted.js | 7 ++++++- packages/rocketchat-i18n/i18n/en.i18n.json | 1 + server/startup/migrations/index.ts | 1 + server/startup/migrations/v255.ts | 9 +++++++++ 5 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 server/startup/migrations/v255.ts diff --git a/app/authorization/server/functions/upsertPermissions.ts b/app/authorization/server/functions/upsertPermissions.ts index abfa562122be..43dceaffc0aa 100644 --- a/app/authorization/server/functions/upsertPermissions.ts +++ b/app/authorization/server/functions/upsertPermissions.ts @@ -174,7 +174,7 @@ export const upsertPermissions = async (): Promise => { }, { _id: 'send-omnichannel-chat-transcript', roles: ['livechat-manager', 'admin'] }, { _id: 'mail-messages', roles: ['admin'] }, - { _id: 'toggle-room-e2e-encryption', roles: ['owner'] }, + { _id: 'toggle-room-e2e-encryption', roles: ['owner', 'admin'] }, { _id: 'message-impersonate', roles: ['bot', 'app'] }, { _id: 'create-team', roles: ['admin', 'user'] }, { _id: 'delete-team', roles: ['admin', 'owner'] }, diff --git a/client/views/room/Header/icons/Encrypted.js b/client/views/room/Header/icons/Encrypted.js index 0c272772e74f..bd2c1c3a910f 100644 --- a/client/views/room/Header/icons/Encrypted.js +++ b/client/views/room/Header/icons/Encrypted.js @@ -3,6 +3,7 @@ import colors from '@rocket.chat/fuselage-tokens/colors'; import React, { memo } from 'react'; import Header from '../../../../components/Header'; +import { usePermission } from '../../../../contexts/AuthorizationContext'; import { useMethod } from '../../../../contexts/ServerContext'; import { useSetting } from '../../../../contexts/SettingsContext'; import { useTranslation } from '../../../../contexts/TranslationContext'; @@ -11,8 +12,12 @@ const Encrypted = ({ room }) => { const t = useTranslation(); const e2eEnabled = useSetting('E2E_Enable'); const toggleE2E = useMethod('saveRoomSettings'); - const encryptedLabel = t('Encrypted'); + const canToggleE2E = usePermission('toggle-room-e2e-encryption'); + const encryptedLabel = canToggleE2E ? t('Encrypted_key_title') : t('Encrypted'); const handleE2EClick = useMutableCallback(() => { + if (!canToggleE2E) { + return; + } toggleE2E(room._id, 'encrypted', !(room && room.encrypted)); }); return e2eEnabled && room?.encrypted ? ( diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 31b8f42013ed..8dc19e52ad45 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -1617,6 +1617,7 @@ "Enabled": "Enabled", "Encrypted": "Encrypted", "Encrypted_channel_Description": "End to end encrypted channel. Search will not work with encrypted channels and notifications may not show the messages content.", + "Encrypted_key_title": "Click here to disable end-to-end encryption for this channel (requires e2ee-permission)", "Encrypted_message": "Encrypted message", "Encrypted_setting_changed_successfully": "Encrypted setting changed successfully", "Encrypted_not_available": "Not available for Public Channels", diff --git a/server/startup/migrations/index.ts b/server/startup/migrations/index.ts index f87385facdcd..a305d2c76751 100644 --- a/server/startup/migrations/index.ts +++ b/server/startup/migrations/index.ts @@ -78,4 +78,5 @@ import './v251'; import './v252'; import './v253'; import './v254'; +import './v255'; import './xrun'; diff --git a/server/startup/migrations/v255.ts b/server/startup/migrations/v255.ts new file mode 100644 index 000000000000..53f0d7f9a6d7 --- /dev/null +++ b/server/startup/migrations/v255.ts @@ -0,0 +1,9 @@ +import { upsertPermissions } from '../../../app/authorization/server/functions/upsertPermissions'; +import { addMigration } from '../../lib/migrations'; + +addMigration({ + version: 255, + up() { + return upsertPermissions(); + }, +}); From 7fed8ce6af680572964df738559bc20cae59384c Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Thu, 17 Feb 2022 23:20:26 +0530 Subject: [PATCH 11/37] [IMPROVE] OTR system messages (#24382) * system messages for otr * more system messages and improvements * otr refresh state * one more system message and lint * js files to ts * requested changes * lint fixes --- app/models/server/models/Messages.js | 19 ++++++++++++++++- app/otr/client/index.js | 1 + app/otr/client/messageTypes.ts | 22 ++++++++++++++++++++ app/otr/client/rocketchat.otr.js | 3 +++ app/otr/client/rocketchat.otr.room.js | 15 ++++++++++++- app/otr/lib/constants.ts | 5 +++++ app/otr/server/index.js | 1 + app/otr/server/methods/sendSystemMessages.ts | 12 +++++++++++ packages/rocketchat-i18n/i18n/en.i18n.json | 3 +++ 9 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 app/otr/client/messageTypes.ts create mode 100644 app/otr/lib/constants.ts create mode 100644 app/otr/server/methods/sendSystemMessages.ts diff --git a/app/models/server/models/Messages.js b/app/models/server/models/Messages.js index 9b7f3b708dff..c25801c062fa 100644 --- a/app/models/server/models/Messages.js +++ b/app/models/server/models/Messages.js @@ -4,6 +4,7 @@ import _ from 'underscore'; import { Base } from './_Base'; import Rooms from './Rooms'; import { settings } from '../../../settings/server/functions/settings'; +import { otrSystemMessages } from '../../../otr/lib/constants'; export class Messages extends Base { constructor() { @@ -105,7 +106,18 @@ export class Messages extends Base { } deleteOldOTRMessages(roomId, ts) { - const query = { rid: roomId, t: 'otr', ts: { $lte: ts } }; + const query = { + rid: roomId, + t: { + $in: [ + 'otr', + otrSystemMessages.USER_JOINED_OTR, + otrSystemMessages.USER_REQUESTED_OTR_KEY_REFRESH, + otrSystemMessages.USER_KEY_REFRESHED_SUCCESSFULLY, + ], + }, + ts: { $lte: ts }, + }; return this.remove(query); } @@ -975,6 +987,11 @@ export class Messages extends Base { return this.createWithTypeRoomIdMessageAndUser('subscription-role-removed', roomId, message, user, extraData); } + createOtrSystemMessagesWithRoomIdAndUser(roomId, user, id, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser(id, roomId, message, user, extraData); + } + // REMOVE removeById(_id) { const query = { _id }; diff --git a/app/otr/client/index.js b/app/otr/client/index.js index a24ea4f03ff6..2f503d293511 100644 --- a/app/otr/client/index.js +++ b/app/otr/client/index.js @@ -2,3 +2,4 @@ import './stylesheets/otr.css'; import './rocketchat.otr.room'; import './rocketchat.otr'; import './tabBar'; +import './messageTypes'; diff --git a/app/otr/client/messageTypes.ts b/app/otr/client/messageTypes.ts new file mode 100644 index 000000000000..24fe9fe12ccf --- /dev/null +++ b/app/otr/client/messageTypes.ts @@ -0,0 +1,22 @@ +import { Meteor } from 'meteor/meteor'; + +import { MessageTypes } from '../../ui-utils/client'; +import { otrSystemMessages } from '../lib/constants'; + +Meteor.startup(() => { + MessageTypes.registerType({ + id: otrSystemMessages.USER_JOINED_OTR, + system: true, + message: otrSystemMessages.USER_JOINED_OTR, + }); + MessageTypes.registerType({ + id: otrSystemMessages.USER_REQUESTED_OTR_KEY_REFRESH, + system: true, + message: otrSystemMessages.USER_REQUESTED_OTR_KEY_REFRESH, + }); + MessageTypes.registerType({ + id: otrSystemMessages.USER_KEY_REFRESHED_SUCCESSFULLY, + system: true, + message: otrSystemMessages.USER_KEY_REFRESHED_SUCCESSFULLY, + }); +}); diff --git a/app/otr/client/rocketchat.otr.js b/app/otr/client/rocketchat.otr.js index 8606fad2fb26..c4eee72e92c6 100644 --- a/app/otr/client/rocketchat.otr.js +++ b/app/otr/client/rocketchat.otr.js @@ -74,6 +74,9 @@ Meteor.startup(function () { message.msg = t('Encrypted_message'); return Promise.resolve(message); } + if (message.t !== 'otr') { + return message; + } const otrRoom = OTR.getInstanceByRoomId(message.rid); return otrRoom.decrypt(message.msg).then((data) => { const { _id, text, ack } = data; diff --git a/app/otr/client/rocketchat.otr.room.js b/app/otr/client/rocketchat.otr.room.js index faaa6a60d3dc..7bdd91e8976b 100644 --- a/app/otr/client/rocketchat.otr.room.js +++ b/app/otr/client/rocketchat.otr.room.js @@ -15,6 +15,7 @@ import { goToRoomById } from '../../../client/lib/utils/goToRoomById'; import { imperativeModal } from '../../../client/lib/imperativeModal'; import GenericModal from '../../../client/components/GenericModal'; import { dispatchToastMessage } from '../../../client/lib/toast'; +import { otrSystemMessages } from '../lib/constants'; OTR.Room = class { constructor(userId, roomId) { @@ -23,6 +24,7 @@ OTR.Room = class { this.peerId = getUidDirectMessage(roomId); this.established = new ReactiveVar(false); this.establishing = new ReactiveVar(false); + this.isFirstOTR = true; this.userOnlineComputation = null; @@ -42,6 +44,10 @@ OTR.Room = class { refresh, }); }); + if (refresh) { + Meteor.call('sendSystemMessages', this.roomId, Meteor.user(), otrSystemMessages.USER_REQUESTED_OTR_KEY_REFRESH); + this.isFirstOTR = false; + } } acknowledge() { @@ -61,6 +67,7 @@ OTR.Room = class { } end() { + this.isFirstOTR = true; this.reset(); Notifications.notifyUser(this.peerId, 'otr', 'end', { roomId: this.roomId, @@ -245,7 +252,6 @@ OTR.Room = class { switch (type) { case 'handshake': let timeout = null; - const establishConnection = () => { this.establishing.set(true); Meteor.clearTimeout(timeout); @@ -256,6 +262,9 @@ OTR.Room = class { Meteor.defer(() => { this.established.set(true); this.acknowledge(); + if (data.refresh) { + Meteor.call('sendSystemMessages', this.roomId, Meteor.user(), otrSystemMessages.USER_KEY_REFRESHED_SUCCESSFULLY); + } }); }); }); @@ -306,6 +315,10 @@ OTR.Room = class { this.importPublicKey(data.publicKey).then(() => { this.established.set(true); }); + if (this.isFirstOTR) { + Meteor.call('sendSystemMessages', this.roomId, Meteor.user(), otrSystemMessages.USER_JOINED_OTR); + } + this.isFirstOTR = false; break; case 'deny': diff --git a/app/otr/lib/constants.ts b/app/otr/lib/constants.ts new file mode 100644 index 000000000000..6f565c80955f --- /dev/null +++ b/app/otr/lib/constants.ts @@ -0,0 +1,5 @@ +export const otrSystemMessages = { + USER_JOINED_OTR: 'user_joined_otr', + USER_REQUESTED_OTR_KEY_REFRESH: 'user_requested_otr_key_refresh', + USER_KEY_REFRESHED_SUCCESSFULLY: 'user_key_refreshed_successfully', +}; diff --git a/app/otr/server/index.js b/app/otr/server/index.js index 1df0c14f3b15..8d489ec9c161 100644 --- a/app/otr/server/index.js +++ b/app/otr/server/index.js @@ -1,3 +1,4 @@ import './settings'; import './methods/deleteOldOTRMessages'; import './methods/updateOTRAck'; +import './methods/sendSystemMessages'; diff --git a/app/otr/server/methods/sendSystemMessages.ts b/app/otr/server/methods/sendSystemMessages.ts new file mode 100644 index 000000000000..e019021f5bc9 --- /dev/null +++ b/app/otr/server/methods/sendSystemMessages.ts @@ -0,0 +1,12 @@ +import { Meteor } from 'meteor/meteor'; + +import { Messages } from '../../../models/server'; + +Meteor.methods({ + sendSystemMessages(rid, user, id) { + if (!Meteor.userId()) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'sendSystemMessages' }); + } + Messages.createOtrSystemMessagesWithRoomIdAndUser(rid, user, id); + }, +}); diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 8dc19e52ad45..c09acb6277cf 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -4480,6 +4480,9 @@ "User_joined_channel_female": "Has joined the channel.", "User_joined_channel_male": "Has joined the channel.", "User_joined_conversation": "Has joined the conversation", + "user_joined_otr": "Has joined OTR chat.", + "user_key_refreshed_successfully": "key refreshed successfully", + "user_requested_otr_key_refresh": "Has requested key refresh.", "User_joined_team": "Has joined the team.", "User_joined_team_female": "Has joined the team.", "User_joined_team_male": "Has joined the team.", From 7cf8a84f8598c0b1abb57ca7bf305e4438a1981d Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 17 Feb 2022 16:16:13 -0300 Subject: [PATCH 12/37] add description to global otr setting (#24333) Co-authored-by: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> --- packages/rocketchat-i18n/i18n/en.i18n.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index c09acb6277cf..3305b7c1c638 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -3277,6 +3277,7 @@ "others": "others", "Others": "Others", "OTR": "OTR", + "OTR_Enable_Description": "Enable option to use off-the-record (OTR) messages in direct messages between 2 users. OTR messages are not recorded on the server and exchanged directly and encrypted between the 2 users.", "OTR_is_only_available_when_both_users_are_online": "OTR is only available when both users are online", "Out_of_seats": "Out of Seats", "Outgoing": "Outgoing", From 888dbdcd1af85c3090e33e3a8c6ae2fdd38b85f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 16:18:27 -0300 Subject: [PATCH 13/37] Bump url-parse from 1.5.3 to 1.5.7 (#24528) Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.3 to 1.5.7. - [Release notes](https://github.com/unshiftio/url-parse/releases) - [Commits](https://github.com/unshiftio/url-parse/compare/1.5.3...1.5.7) --- updated-dependencies: - dependency-name: url-parse dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 981758c9c00b..2efd63fbc841 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37047,9 +37047,9 @@ } }, "url-parse": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz", - "integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==", + "version": "1.5.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz", + "integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==", "requires": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" From a1095a464b0fd6a528cf1819ec1ff9054cd1bfee Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Thu, 17 Feb 2022 16:39:36 -0300 Subject: [PATCH 14/37] Chore: Remove storybook build job from CI (#24530) --- .github/workflows/build_and_test.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 6ca7c176d815..ac1da992f7be 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -119,10 +119,6 @@ jobs: - run: meteor npm run typecheck - - name: Build Storybook to sanity check components - run: npm run build-storybook ; rm -rf ./storybook-static - - # To reduce memory need during actual build, build the packages solely first # - name: Build a Meteor cache # run: | From 9fd4c04d74e40ddc8a875657353cc502d9a2a664 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Thu, 17 Feb 2022 17:19:41 -0300 Subject: [PATCH 15/37] [FIX] Read receipts showing first messages of the room as read even if not read by everyone (#24508) Co-authored-by: dougfabris --- app/models/server/models/Subscriptions.js | 3 -- client/hooks/useFormatDateAndTime.js | 24 --------------- client/hooks/useFormatDateAndTime.ts | 29 +++++++++++++++++++ .../views/account/tokens/AccountTokensRow.tsx | 3 +- .../ReadReceiptsModal/ReadReceiptRow.tsx | 2 +- imports/message-read-receipt/server/hooks.js | 2 +- .../server/lib/ReadReceipt.js | 25 ++++++++-------- 7 files changed, 46 insertions(+), 42 deletions(-) delete mode 100644 client/hooks/useFormatDateAndTime.js create mode 100644 client/hooks/useFormatDateAndTime.ts diff --git a/app/models/server/models/Subscriptions.js b/app/models/server/models/Subscriptions.js index deddc06afc5a..74e2176446ef 100644 --- a/app/models/server/models/Subscriptions.js +++ b/app/models/server/models/Subscriptions.js @@ -556,9 +556,6 @@ export class Subscriptions extends Base { return this.db.findOne( { rid, - ls: { - $exists: true, - }, }, { sort: { diff --git a/client/hooks/useFormatDateAndTime.js b/client/hooks/useFormatDateAndTime.js deleted file mode 100644 index 900e832273ad..000000000000 --- a/client/hooks/useFormatDateAndTime.js +++ /dev/null @@ -1,24 +0,0 @@ -import moment from 'moment'; -import { useCallback } from 'react'; - -import { useSetting } from '../contexts/SettingsContext'; -import { useUserPreference } from '../contexts/UserContext'; - -export const useFormatDateAndTime = () => { - const clockMode = useUserPreference('clockMode', false); - const format = useSetting('Message_TimeAndDateFormat'); - - return useCallback( - (time) => { - switch (clockMode) { - case 1: - return moment(time).format('MMMM D, Y h:mm A'); - case 2: - return moment(time).format('MMMM D, Y H:mm'); - default: - return moment(time).format(format); - } - }, - [clockMode, format], - ); -}; diff --git a/client/hooks/useFormatDateAndTime.ts b/client/hooks/useFormatDateAndTime.ts new file mode 100644 index 000000000000..0bda2b2350e4 --- /dev/null +++ b/client/hooks/useFormatDateAndTime.ts @@ -0,0 +1,29 @@ +import moment, { MomentInput } from 'moment'; +import { useCallback } from 'react'; + +import { useSetting } from '../contexts/SettingsContext'; +import { useUserPreference } from '../contexts/UserContext'; + +type UseFormatDateAndTimeParams = { + withSeconds?: boolean; +}; + +export const useFormatDateAndTime = ({ withSeconds }: UseFormatDateAndTimeParams = {}): ((input: MomentInput) => string) => { + const clockMode = useUserPreference('clockMode'); + const format = useSetting('Message_TimeAndDateFormat') as string; + + return useCallback( + (time) => { + switch (clockMode) { + case 1: + return moment(time).format(withSeconds ? 'MMMM D, Y h:mm:ss A' : 'MMMM D, Y h:mm A'); + case 2: + return moment(time).format(withSeconds ? 'MMMM D, Y H:mm:ss' : 'MMMM D, Y H:mm'); + + default: + return moment(time).format(withSeconds ? 'L LTS' : format); + } + }, + [clockMode, format, withSeconds], + ); +}; diff --git a/client/views/account/tokens/AccountTokensRow.tsx b/client/views/account/tokens/AccountTokensRow.tsx index d64153d507f6..7f6f93bd0c25 100644 --- a/client/views/account/tokens/AccountTokensRow.tsx +++ b/client/views/account/tokens/AccountTokensRow.tsx @@ -1,4 +1,5 @@ import { Button, ButtonGroup, Icon, Table } from '@rocket.chat/fuselage'; +import type { MomentInput } from 'moment'; import React, { useCallback, FC } from 'react'; import { useTranslation } from '../../../contexts/TranslationContext'; @@ -6,7 +7,7 @@ import { useFormatDateAndTime } from '../../../hooks/useFormatDateAndTime'; type AccountTokensRowProps = { bypassTwoFactor: unknown; - createdAt: unknown; + createdAt: MomentInput; isMedium: boolean; lastTokenPart: string; name: string; diff --git a/client/views/room/modals/ReadReceiptsModal/ReadReceiptRow.tsx b/client/views/room/modals/ReadReceiptsModal/ReadReceiptRow.tsx index 0c26e718eb1a..d06f8207e74c 100644 --- a/client/views/room/modals/ReadReceiptsModal/ReadReceiptRow.tsx +++ b/client/views/room/modals/ReadReceiptsModal/ReadReceiptRow.tsx @@ -16,7 +16,7 @@ const hoverStyle = css` const ReadReceiptRow = ({ user, ts }: ReadReceipt): ReactElement => { const displayName = useUserDisplayName(user); - const formatDateAndTime = useFormatDateAndTime(); + const formatDateAndTime = useFormatDateAndTime({ withSeconds: true }); return ( { // @TODO maybe store firstSubscription in room object so we don't need to call the above update method const firstSubscription = Subscriptions.getMinimumLastSeenByRoomId(_id); - if (!firstSubscription) { + if (!firstSubscription || !firstSubscription.ls) { return; } @@ -42,31 +42,32 @@ export const ReadReceipt = { const room = Rooms.findOneById(roomId, { fields: { lm: 1 } }); - // if users last seen is greadebounceByRoomIdter than room's last message, it means the user already have this room marked as read + // if users last seen is greater than room's last message, it means the user already have this room marked as read if (userLastSeen > room.lm) { return; } - if (userLastSeen) { - this.storeReadReceipts(Messages.findUnreadMessagesByRoomAndDate(roomId, userLastSeen), roomId, userId); - } + this.storeReadReceipts(Messages.findUnreadMessagesByRoomAndDate(roomId, userLastSeen), roomId, userId); updateMessages(room); }, - markMessageAsReadBySender(message, roomId, userId) { + markMessageAsReadBySender(message, { _id: roomId, t }, userId) { if (!settings.get('Message_Read_Receipt_Enabled')) { return; } - // this will usually happens if the message sender is the only one on the room - const firstSubscription = Subscriptions.getMinimumLastSeenByRoomId(roomId); - if (firstSubscription && message.unread && message.ts < firstSubscription.ls) { - Messages.setAsReadById(message._id, firstSubscription.ls); + if (!message.unread) { + return; + } + + // mark message as read if the sender is the only one in the room + const isUserAlone = Subscriptions.findByRoomIdAndNotUserId(roomId, userId, { fields: { _id: 1 } }).count() === 0; + if (isUserAlone) { + Messages.setAsReadById(message._id); } - const room = Rooms.findOneById(roomId, { fields: { t: 1 } }); - const extraData = roomTypes.getConfig(room.t).getReadReceiptsExtraData(message); + const extraData = roomTypes.getConfig(t).getReadReceiptsExtraData(message); this.storeReadReceipts([{ _id: message._id }], roomId, userId, extraData); }, From 2f6175442a46b817723dade524af9c9d8c784737 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 17:35:41 -0300 Subject: [PATCH 16/37] Bump date-fns from 2.24.0 to 2.28.0 (#24058) Bumps [date-fns](https://github.com/date-fns/date-fns) from 2.24.0 to 2.28.0. - [Release notes](https://github.com/date-fns/date-fns/releases) - [Changelog](https://github.com/date-fns/date-fns/blob/master/CHANGELOG.md) - [Commits](https://github.com/date-fns/date-fns/compare/v2.24.0...v2.28.0) --- updated-dependencies: - dependency-name: date-fns dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2efd63fbc841..9eb2fb47c647 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16723,9 +16723,9 @@ "integrity": "sha512-lcWy3AXDRJOD7MplwZMmNSRM//kZtJaLz4n6D1P5z9wEmZGBKhJRBIr1Xs9KNQJmdXPblvgffynYji4iylUTcA==" }, "date-fns": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.24.0.tgz", - "integrity": "sha512-6ujwvwgPID6zbI0o7UbURi2vlLDR9uP26+tW6Lg+Ji3w7dd0i3DOcjcClLjLPranT60SSEFBwdSyYwn/ZkPIuw==" + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", + "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==" }, "date.js": { "version": "0.3.3", diff --git a/package.json b/package.json index 22a3c1f66bc4..8caf9df6e2e9 100644 --- a/package.json +++ b/package.json @@ -222,7 +222,7 @@ "cors": "^2.8.5", "css-vars-ponyfill": "^2.4.7", "csv-parse": "^4.16.3", - "date-fns": "^2.23.0", + "date-fns": "^2.28.0", "dompurify": "^2.2.9", "ejson": "^2.2.1", "emailreplyparser": "^0.0.5", From 467261fe9c323e961a2de57f2219a5e81233797d Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Thu, 17 Feb 2022 18:49:46 -0300 Subject: [PATCH 17/37] Chore: Improve PR title validation regex (#24467) Co-authored-by: Debdut Chakraborty --- .github/pr-title-checker-config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pr-title-checker-config.json b/.github/pr-title-checker-config.json index 3b992e891884..07089e99314a 100644 --- a/.github/pr-title-checker-config.json +++ b/.github/pr-title-checker-config.json @@ -4,7 +4,7 @@ "color": "B60205" }, "CHECKS": { - "regexp": "^(?:(?:\\[(NEW|BREAK|IMPROVE|FIX)\\](\\[(ENTERPRISE|APPS)\\])?|(?:Regression|Chore|Revert|i18n):)|(?:Bump) .+|Release [0-9]+\\.[0-9]+\\.[0-9]+|Merge master into develop)", + "regexp": "^(?:(?:\\[(NEW|BREAK|IMPROVE|FIX)\\](\\[(ENTERPRISE|APPS)\\])?|(?:Regression|Chore|Revert|i18n):)|(?:Bump)) .+$|^Release [0-9]+\\.[0-9]+\\.[0-9]+$|^Merge master into develop", "ignoreLabels" : ["[ignore-title]"] } } From bfaa1dbe0c532fedf1e1f2c0903e545025b7b8eb Mon Sep 17 00:00:00 2001 From: Leonardo Ostjen Couto Date: Thu, 17 Feb 2022 21:37:03 -0300 Subject: [PATCH 18/37] [IMPROVE] Team system messages feedback (#24209) --- app/api/server/v1/channels.js | 4 +- app/api/server/v1/teams.ts | 14 ++- app/lib/lib/MessageTypes.js | 106 ++++++++++++++++++ app/lib/server/functions/addUserToRoom.js | 9 +- app/lib/server/functions/createRoom.js | 7 +- .../server/functions/removeUserFromRoom.js | 10 +- app/models/server/models/Messages.js | 30 +++++ app/theme/client/imports/general/base_old.css | 2 - app/ui-message/client/message.html | 7 ++ .../server/cronProcessDownloads.js | 38 +++++++ .../Info/RoomInfo/RoomInfoWithData.js | 2 +- packages/rocketchat-i18n/i18n/en.i18n.json | 47 ++++---- packages/rocketchat-i18n/i18n/pt-BR.i18n.json | 2 +- server/methods/eraseRoom.js | 9 +- server/sdk/types/ITeamService.ts | 2 +- server/services/team/service.ts | 29 ++++- 16 files changed, 272 insertions(+), 46 deletions(-) diff --git a/app/api/server/v1/channels.js b/app/api/server/v1/channels.js index 24b3da99e45f..371a2de3d1c2 100644 --- a/app/api/server/v1/channels.js +++ b/app/api/server/v1/channels.js @@ -306,13 +306,13 @@ API.v1.addRoute( { authRequired: true }, { post() { - const findResult = findChannelByIdOrName({ + const room = findChannelByIdOrName({ params: this.requestParams(), checkedArchived: false, }); Meteor.runAsUser(this.userId, () => { - Meteor.call('eraseRoom', findResult._id); + Meteor.call('eraseRoom', room._id); }); return API.v1.success(); diff --git a/app/api/server/v1/teams.ts b/app/api/server/v1/teams.ts index 4407071d31cc..476a933febca 100644 --- a/app/api/server/v1/teams.ts +++ b/app/api/server/v1/teams.ts @@ -140,7 +140,11 @@ API.v1.addRoute( }); } - await Promise.all([Team.unsetTeamIdOfRooms(team._id), Team.removeAllMembersFromTeam(team._id), Team.deleteById(team._id)]); + await Promise.all([ + Team.unsetTeamIdOfRooms(this.userId, team._id), + Team.removeAllMembersFromTeam(team._id), + Team.deleteById(team._id), + ]); return API.v1.success(); }, @@ -606,9 +610,6 @@ API.v1.addRoute( const rooms: string[] = await Team.getMatchingTeamRooms(team._id, roomsToRemove); - // Remove the team's main room - Meteor.call('eraseRoom', team.roomId); - // If we got a list of rooms to delete along with the team, remove them first if (rooms.length) { rooms.forEach((room) => { @@ -617,7 +618,10 @@ API.v1.addRoute( } // Move every other room back to the workspace - await Team.unsetTeamIdOfRooms(team._id); + await Team.unsetTeamIdOfRooms(this.userId, team._id); + + // Remove the team's main room + Meteor.call('eraseRoom', team.roomId); // Delete all team memberships Team.removeAllMembersFromTeam(team._id); diff --git a/app/lib/lib/MessageTypes.js b/app/lib/lib/MessageTypes.js index 19bcc537dd72..3915ec8f0dbb 100644 --- a/app/lib/lib/MessageTypes.js +++ b/app/lib/lib/MessageTypes.js @@ -26,6 +26,16 @@ Meteor.startup(function () { }; }, }); + MessageTypes.registerType({ + id: 'added-user-to-team', + system: true, + message: 'Added__username__to_team', + data(message) { + return { + user_added: message.msg, + }; + }, + }); MessageTypes.registerType({ id: 'ru', system: true, @@ -37,6 +47,16 @@ Meteor.startup(function () { }; }, }); + MessageTypes.registerType({ + id: 'removed-user-from-team', + system: true, + message: 'Removed__username__from_team', + data(message) { + return { + user_removed: message.msg, + }; + }, + }); MessageTypes.registerType({ id: 'ul', system: true, @@ -57,6 +77,56 @@ Meteor.startup(function () { }; }, }); + MessageTypes.registerType({ + id: 'user-converted-to-team', + system: true, + message: 'Converted__roomName__to_team', + data(message) { + return { + roomName: message.msg, + }; + }, + }); + MessageTypes.registerType({ + id: 'user-converted-to-channel', + system: true, + message: 'Converted__roomName__to_channel', + data(message) { + return { + roomName: message.msg, + }; + }, + }); + MessageTypes.registerType({ + id: 'user-removed-room-from-team', + system: true, + message: 'Removed__roomName__from_this_team', + data(message) { + return { + roomName: message.msg, + }; + }, + }); + MessageTypes.registerType({ + id: 'user-deleted-room-from-team', + system: true, + message: 'Deleted__roomName__', + data(message) { + return { + roomName: message.msg, + }; + }, + }); + MessageTypes.registerType({ + id: 'user-added-room-to-team', + system: true, + message: 'added__roomName__to_team', + data(message) { + return { + roomName: message.msg, + }; + }, + }); MessageTypes.registerType({ id: 'uj', system: true, @@ -246,18 +316,34 @@ export const MessageTypesValues = [ key: 'uj', i18nLabel: 'Message_HideType_uj', }, + { + key: 'ujt', + i18nLabel: 'Message_HideType_ujt', + }, { key: 'ul', i18nLabel: 'Message_HideType_ul', }, + { + key: 'ult', + i18nLabel: 'Message_HideType_ult', + }, { key: 'ru', i18nLabel: 'Message_HideType_ru', }, + { + key: 'removed-user-from-team', + i18nLabel: 'Message_HideType_removed_user_from_team', + }, { key: 'au', i18nLabel: 'Message_HideType_au', }, + { + key: 'added-user-to-team', + i18nLabel: 'Message_HideType_added_user_to_team', + }, { key: 'mute_unmute', i18nLabel: 'Message_HideType_mute_unmute', @@ -330,4 +416,24 @@ export const MessageTypesValues = [ key: 'room-allowed-reacting', i18nLabel: 'Message_HideType_room_allowed_reacting', }, + { + key: 'user-added-room-to-team', + i18nLabel: 'Message_HideType_user_added_room_to_team', + }, + { + key: 'user-converted-to-channel', + i18nLabel: 'Message_HideType_user_converted_to_channel', + }, + { + key: 'user-converted-to-team', + i18nLabel: 'Message_HideType_user_converted_to_team', + }, + { + key: 'user-deleted-room-from-team', + i18nLabel: 'Message_HideType_user_deleted_room_from_team', + }, + { + key: 'user-removed-room-from-team', + i18nLabel: 'Message_HideType_user_removed_room_from_team', + }, ]; diff --git a/app/lib/server/functions/addUserToRoom.js b/app/lib/server/functions/addUserToRoom.js index 40e356846341..26c7dc51d514 100644 --- a/app/lib/server/functions/addUserToRoom.js +++ b/app/lib/server/functions/addUserToRoom.js @@ -61,13 +61,18 @@ export const addUserToRoom = function (rid, user, inviter, silenced) { if (!silenced) { if (inviter) { - Messages.createUserAddedWithRoomIdAndUser(rid, user, { + const extraData = { ts: now, u: { _id: inviter._id, username: inviter.username, }, - }); + }; + if (room.teamMain) { + Messages.createUserAddedToTeamWithRoomIdAndUser(rid, user, extraData); + } else { + Messages.createUserAddedWithRoomIdAndUser(rid, user, extraData); + } } else if (room.prid) { Messages.createUserJoinWithRoomIdAndUserDiscussion(rid, user, { ts: now }); } else if (room.teamMain) { diff --git a/app/lib/server/functions/createRoom.js b/app/lib/server/functions/createRoom.js index 2569f8ea5df0..96cd49c579d0 100644 --- a/app/lib/server/functions/createRoom.js +++ b/app/lib/server/functions/createRoom.js @@ -6,7 +6,7 @@ import s from 'underscore.string'; import { Apps } from '../../../apps/server'; import { addUserRoles } from '../../../authorization'; import { callbacks } from '../../../../lib/callbacks'; -import { Rooms, Subscriptions, Users } from '../../../models'; +import { Messages, Rooms, Subscriptions, Users } from '../../../models'; import { getValidRoomName } from '../../../utils'; import { createDirectRoom } from './createDirectRoom'; import { Team } from '../../../../server/sdk'; @@ -131,6 +131,11 @@ export const createRoom = function (type, name, owner, members = [], readOnly, { addUserRoles(owner._id, ['owner'], room._id); + if (room.teamId) { + const team = Promise.await(Team.getOneById(room.teamId)); + Messages.createUserAddRoomToTeamWithRoomIdAndUser(team.roomId, room.name, owner); + } + if (type === 'c') { Meteor.defer(() => { callbacks.run('afterCreateChannel', owner, room); diff --git a/app/lib/server/functions/removeUserFromRoom.js b/app/lib/server/functions/removeUserFromRoom.js index 32eea62a5215..9745a69bc42b 100644 --- a/app/lib/server/functions/removeUserFromRoom.js +++ b/app/lib/server/functions/removeUserFromRoom.js @@ -29,9 +29,15 @@ export const removeUserFromRoom = function (rid, user, options = {}) { if (subscription) { const removedUser = user; if (options.byUser) { - Messages.createUserRemovedWithRoomIdAndUser(rid, user, { + const extraData = { u: options.byUser, - }); + }; + + if (room.teamMain) { + Messages.createUserRemovedFromTeamWithRoomIdAndUser(rid, user, extraData); + } else { + Messages.createUserRemovedWithRoomIdAndUser(rid, user, extraData); + } } else if (room.teamMain) { Messages.createUserLeaveTeamWithRoomIdAndUser(rid, removedUser); } else { diff --git a/app/models/server/models/Messages.js b/app/models/server/models/Messages.js index c25801c062fa..dae73072ba18 100644 --- a/app/models/server/models/Messages.js +++ b/app/models/server/models/Messages.js @@ -923,16 +923,46 @@ export class Messages extends Base { return this.createWithTypeRoomIdMessageAndUser('ult', roomId, message, user, extraData); } + createUserConvertChannelToTeamWithRoomIdAndUser(roomId, roomName, user, extraData) { + return this.createWithTypeRoomIdMessageAndUser('user-converted-to-team', roomId, roomName, user, extraData); + } + + createUserConvertTeamToChannelWithRoomIdAndUser(roomId, roomName, user, extraData) { + return this.createWithTypeRoomIdMessageAndUser('user-converted-to-channel', roomId, roomName, user, extraData); + } + + createUserRemoveRoomFromTeamWithRoomIdAndUser(roomId, roomName, user, extraData) { + return this.createWithTypeRoomIdMessageAndUser('user-removed-room-from-team', roomId, roomName, user, extraData); + } + + createUserDeleteRoomFromTeamWithRoomIdAndUser(roomId, roomName, user, extraData) { + return this.createWithTypeRoomIdMessageAndUser('user-deleted-room-from-team', roomId, roomName, user, extraData); + } + + createUserAddRoomToTeamWithRoomIdAndUser(roomId, roomName, user, extraData) { + return this.createWithTypeRoomIdMessageAndUser('user-added-room-to-team', roomId, roomName, user, extraData); + } + createUserRemovedWithRoomIdAndUser(roomId, user, extraData) { const message = user.username; return this.createWithTypeRoomIdMessageAndUser('ru', roomId, message, user, extraData); } + createUserRemovedFromTeamWithRoomIdAndUser(roomId, user, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser('removed-user-from-team', roomId, message, user, extraData); + } + createUserAddedWithRoomIdAndUser(roomId, user, extraData) { const message = user.username; return this.createWithTypeRoomIdMessageAndUser('au', roomId, message, user, extraData); } + createUserAddedToTeamWithRoomIdAndUser(roomId, user, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser('added-user-to-team', roomId, message, user, extraData); + } + createCommandWithRoomIdAndUser(command, roomId, user, extraData) { return this.createWithTypeRoomIdMessageAndUser('command', roomId, command, user, extraData); } diff --git a/app/theme/client/imports/general/base_old.css b/app/theme/client/imports/general/base_old.css index 687adb5be2a0..3563ba9a5435 100644 --- a/app/theme/client/imports/general/base_old.css +++ b/app/theme/client/imports/general/base_old.css @@ -1880,8 +1880,6 @@ & .user { display: inline-block; - margin-right: 5px; - font-family: inherit; font-size: 0.875rem; diff --git a/app/ui-message/client/message.html b/app/ui-message/client/message.html index b94d7a22e262..e7c563c3cc4f 100644 --- a/app/ui-message/client/message.html +++ b/app/ui-message/client/message.html @@ -49,7 +49,9 @@ {{#if isBot}} {{_ "Bot"}} {{/if}} + {{#unless system}} + {{/unless}} {{#if showTranslated}} @@ -111,6 +113,11 @@ {{> messageLocation location=msg.location}} {{/if}} + {{#if system}} +
+ +
+ {{/if}} {{#if hasOembed}} diff --git a/app/user-data-download/server/cronProcessDownloads.js b/app/user-data-download/server/cronProcessDownloads.js index e4b5df7a4a2a..c93272dc1e31 100644 --- a/app/user-data-download/server/cronProcessDownloads.js +++ b/app/user-data-download/server/cronProcessDownloads.js @@ -165,12 +165,45 @@ const getMessageData = function (msg, hideUsers, userData, usersMap) { case 'ult': messageObject.msg = TAPi18n.__('User_left_team'); break; + case 'user-added-room-to-team': + messageObject.msg = TAPi18n.__('added__roomName__to_team', { + roomName: msg.msg, + }); + break; + case 'user-converted-to-team': + messageObject.msg = TAPi18n.__('Converted__roomName__to_team', { + roomName: msg.msg, + }); + break; + case 'user-converted-to-channel': + messageObject.msg = TAPi18n.__('Converted__roomName__to_channel', { + roomName: msg.msg, + }); + break; + case 'user-deleted-room-from-team': + messageObject.msg = TAPi18n.__('Deleted__roomName__', { + roomName: msg.msg, + }); + break; + case 'user-removed-room-from-team': + messageObject.msg = TAPi18n.__('Removed__roomName__from_this_team', { + roomName: msg.msg, + }); + break; + case 'ujt': + messageObject.msg = TAPi18n.__('User_joined_team'); + break; case 'au': messageObject.msg = TAPi18n.__('User_added_by', { user_added: hideUserName(msg.msg, userData, usersMap), user_by: username, }); break; + case 'added-user-to-team': + messageObject.msg = TAPi18n.__('Added__username__to_team', { + user_added: msg.msg, + }); + break; case 'r': messageObject.msg = TAPi18n.__('Room_name_changed', { room_name: msg.msg, @@ -183,6 +216,11 @@ const getMessageData = function (msg, hideUsers, userData, usersMap) { user_by: username, }); break; + case 'removed-user-from-team': + messageObject.msg = TAPi18n.__('Removed__username__from_team', { + user_removed: hideUserName(msg.msg, userData, usersMap), + }); + break; case 'wm': messageObject.msg = TAPi18n.__('Welcome', { user: username }); break; diff --git a/client/views/room/contextualBar/Info/RoomInfo/RoomInfoWithData.js b/client/views/room/contextualBar/Info/RoomInfo/RoomInfoWithData.js index 18123cf657a6..fd07b31967a7 100644 --- a/client/views/room/contextualBar/Info/RoomInfo/RoomInfoWithData.js +++ b/client/views/room/contextualBar/Info/RoomInfo/RoomInfoWithData.js @@ -57,7 +57,7 @@ const RoomInfoWithData = ({ rid, openEditing, onClickBack, onEnterRoom, resetSta const leaveRoom = useMethod('leaveRoom'); const router = useRoute('home'); - const moveChannelToTeam = useEndpointActionExperimental('POST', 'teams.addRooms', t('Success')); + const moveChannelToTeam = useEndpointActionExperimental('POST', 'teams.addRooms', t('Rooms_added_successfully')); const convertRoomToTeam = useEndpointActionExperimental( 'POST', type === 'c' ? 'channels.convertToTeam' : 'groups.convertToTeam', diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 3305b7c1c638..af608011732c 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -284,6 +284,8 @@ "add-user-to-any-p-room_description": "Permission to add a user to any private channel", "add-user-to-joined-room": "Add User to Any Joined Channel", "add-user-to-joined-room_description": "Permission to add a user to a currently joined channel", + "added__roomName__to_team": "added #__roomName__ to this Team", + "Added__username__to_team": "added @__user_added__ to this Team", "Adding_OAuth_Services": "Adding OAuth Services", "Adding_permission": "Adding permission", "Adding_user": "Adding user", @@ -617,10 +619,6 @@ "Avg_response_time": "Average of Response Time", "away": "away", "Away": "Away", - "away_female": "away", - "Away_female": "Away", - "away_male": "away", - "Away_male": "Away", "Back": "Back", "Back_to_applications": "Back to applications", "Back_to_chat": "Back to chat", @@ -698,10 +696,6 @@ "Business_hours_updated": "Business hours updated", "busy": "busy", "Busy": "Busy", - "busy_female": "busy", - "Busy_female": "Busy", - "busy_male": "busy", - "Busy_male": "Busy", "By": "By", "by": "by", "By_author": "By __author__", @@ -774,7 +768,7 @@ "Channel_to_listen_on": "Channel to listen on", "Channel_Unarchived": "Channel with name `#%s` has been Unarchived successfully", "Channels": "Channels", - "Channels_added": "Channels added", + "Channels_added": "Channels added sucessfully", "Channels_are_where_your_team_communicate": "Channels are where your team communicate", "Channels_list": "List of public channels", "Channel_what_is_this_channel_about": "What is this channel about?", @@ -955,8 +949,6 @@ "Compact": "Compact", "Condensed": "Condensed", "Condition": "Condition", - "Convert_to_channel": "Convert to Channel", - "Converting_team_to_channel": "Converting Team to Channel", "Commit_details": "Commit Details", "Completed": "Completed", "Computer": "Computer", @@ -1004,7 +996,11 @@ "Conversations_per_day": "Conversations per Day", "Convert": "Convert", "Convert_Ascii_Emojis": "Convert ASCII to Emoji", + "Convert_to_channel": "Convert to Channel", "Converting_channel_to_a_team": "You are converting this Channel to a Team. All members will be kept.", + "Converted__roomName__to_team": "converted #__roomName__ to a Team", + "Converted__roomName__to_channel": "converted #__roomName__ to a Channel", + "Converting_team_to_channel": "Converting Team to Channel", "Copied": "Copied", "Copy": "Copy", "Copy_text": "Copy Text", @@ -1399,6 +1395,7 @@ "delete-user": "Delete User", "delete-user_description": "Permission to delete users", "Deleted": "Deleted!", + "Deleted__roomName__": "deleted #__roomName__", "Department": "Department", "Department_name": "Department name", "Department_not_found": "Department not found", @@ -2329,7 +2326,7 @@ "Invite_user_to_join_channel": "Invite one user to join this channel", "Invite_user_to_join_channel_all_from": "Invite all users from [#channel] to join this channel", "Invite_user_to_join_channel_all_to": "Invite all users from this channel to join [#channel]", - "Invite_Users": "Invite Users", + "Invite_Users": "Invite Members", "IP": "IP", "IRC_Channel_Join": "Output of the JOIN command.", "IRC_Channel_Leave": "Output of the PART command.", @@ -2348,8 +2345,6 @@ "IRC_Private_Message": "Output of the PRIVMSG command.", "IRC_Quit": "Output upon quitting an IRC session.", "is_typing": "is typing", - "is_typing_female": "is typing", - "is_typing_male": "is typing", "Issue_Links": "Issue tracker links", "IssueLinks_Incompatible": "Warning: do not enable this and the 'Hex Color Preview' at the same time.", "IssueLinks_LinkTemplate": "Template for issue links", @@ -2924,6 +2919,7 @@ "Message_has_been_unpinned": "Message has been unpinned", "Message_has_been_unstarred": "Message has been unstarred", "Message_HideType_au": "Hide \"User Added\" messages", + "Message_HideType_added_user_to_team": "Hide \"User Added to Team\" messages", "Message_HideType_mute_unmute": "Hide \"User Muted / Unmuted\" messages", "Message_HideType_r": "Hide \"Room Name Changed\" messages", "Message_HideType_rm": "Hide \"Message Removed\" messages", @@ -2939,10 +2935,18 @@ "Message_HideType_room_removed_read_only": "Hide \"Room added writing permission\" messages", "Message_HideType_room_unarchived": "Hide \"Room Unarchived\" messages", "Message_HideType_ru": "Hide \"User Removed\" messages", + "Message_HideType_removed_user_from_team": "Hide \"User Removed from Team\" messages", "Message_HideType_subscription_role_added": "Hide \"Was Set Role\" messages", "Message_HideType_subscription_role_removed": "Hide \"Role No Longer Defined\" messages", "Message_HideType_uj": "Hide \"User Join\" messages", + "Message_HideType_ujt": "Hide \"User Joined Team\" messages", "Message_HideType_ul": "Hide \"User Leave\" messages", + "Message_HideType_ult": "Hide \"User Left Team\" messages", + "Message_HideType_user_added_room_to_team": "Hide \"User Added Room to Team\" messages", + "Message_HideType_user_converted_to_channel": "Hide \"User converted team to a Channel\" messages", + "Message_HideType_user_converted_to_team": "Hide \"User converted channel to a Team\" messages", + "Message_HideType_user_deleted_room_from_team": "Hide \"User deleted room from Team\" messages", + "Message_HideType_user_removed_room_from_team": "Hide \"User removed room from Team\" messages", "Message_HideType_ut": "Hide \"User Joined Conversation\" messages", "Message_HideType_wm": "Hide \"Welcome\" messages", "Message_Id": "Message Id", @@ -3527,6 +3531,8 @@ "remove-user_description": "Permission to remove a user from a room", "Removed": "Removed", "Removed_User": "Removed User", + "Removed__roomName__from_this_team": "removed #__roomName__ from this Team", + "Removed__username__from_team": "removed @__user_removed__ from this Team", "Replay": "Replay", "Replied_on": "Replied on", "Replies": "Replies", @@ -3670,6 +3676,7 @@ "Room_uploaded_file_list": "Files List", "Room_uploaded_file_list_empty": "No files available.", "Rooms": "Rooms", + "Rooms_added_successfully": "Rooms added successfully", "Routing": "Routing", "Run_only_once_for_each_visitor": "Run only once for each visitor", "run-import": "Run Import", @@ -4478,21 +4485,13 @@ "User_is_now_an_admin": "User is now an admin", "User_is_unblocked": "User is unblocked", "User_joined_channel": "Has joined the channel.", - "User_joined_channel_female": "Has joined the channel.", - "User_joined_channel_male": "Has joined the channel.", "User_joined_conversation": "Has joined the conversation", + "User_joined_team": "joined this Team", "user_joined_otr": "Has joined OTR chat.", "user_key_refreshed_successfully": "key refreshed successfully", "user_requested_otr_key_refresh": "Has requested key refresh.", - "User_joined_team": "Has joined the team.", - "User_joined_team_female": "Has joined the team.", - "User_joined_team_male": "Has joined the team.", "User_left": "Has left the channel.", - "User_left_female": "Has left the channel.", - "User_left_male": "Has left the channel.", - "User_left_team": "Has left the team.", - "User_left_team_female": "Has left the team.", - "User_left_team_male": "Has left the team.", + "User_left_team": "left this Team", "User_logged_out": "User is logged out", "User_management": "User Management", "User_mentions_only": "User mentions only", diff --git a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json index eefecaa4028a..9a5523ac5490 100644 --- a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -2327,7 +2327,7 @@ "Invite_user_to_join_channel": "Convidar um usuário para este canal", "Invite_user_to_join_channel_all_from": "Convide todos os usuários de [#channel] a participar deste canal", "Invite_user_to_join_channel_all_to": "Convide todos os usuários deste canal a participar [#canal]", - "Invite_Users": "Convidar Usuários", + "Invite_Users": "Convidar Membros", "IP": "IP", "IRC_Channel_Join": "Saída do comando JOIN.", "IRC_Channel_Leave": "Saída do comando PART.", diff --git a/server/methods/eraseRoom.js b/server/methods/eraseRoom.js index f5d8d2261d84..95dae4a02c7c 100644 --- a/server/methods/eraseRoom.js +++ b/server/methods/eraseRoom.js @@ -3,9 +3,10 @@ import { check } from 'meteor/check'; import { deleteRoom } from '../../app/lib'; import { hasPermission } from '../../app/authorization'; -import { Rooms } from '../../app/models'; +import { Rooms, Messages } from '../../app/models'; import { Apps } from '../../app/apps/server'; import { roomTypes } from '../../app/utils'; +import { Team } from '../sdk'; Meteor.methods({ eraseRoom(rid) { @@ -40,6 +41,12 @@ Meteor.methods({ const result = deleteRoom(rid); + if (room.teamId) { + const team = Promise.await(Team.getOneById(room.teamId)); + const user = Meteor.user(); + Messages.createUserDeleteRoomFromTeamWithRoomIdAndUser(team.roomId, room.name, user); + } + if (Apps && Apps.isLoaded()) { Apps.getBridges().getListenerBridge().roomEvent('IPostRoomDeleted', room); } diff --git a/server/sdk/types/ITeamService.ts b/server/sdk/types/ITeamService.ts index 1ab8016e0260..c94baedefb73 100644 --- a/server/sdk/types/ITeamService.ts +++ b/server/sdk/types/ITeamService.ts @@ -94,7 +94,7 @@ export interface ITeamService { getInfoById(teamId: string): Promise | null>; deleteById(teamId: string): Promise; deleteByName(teamName: string): Promise; - unsetTeamIdOfRooms(teamId: string): void; + unsetTeamIdOfRooms(uid: string, teamId: string): void; getOneById(teamId: string, options?: FindOneOptions): Promise; getOneById

(teamId: string, options?: FindOneOptions

): Promise; getOneByName(teamName: string | RegExp, options?: FindOneOptions): Promise; diff --git a/server/services/team/service.ts b/server/services/team/service.ts index a8ff78cbcff9..0a4c87e6c508 100644 --- a/server/services/team/service.ts +++ b/server/services/team/service.ts @@ -15,6 +15,7 @@ import { UsersRaw } from '../../../app/models/server/raw/Users'; import { IRoom } from '../../../definition/IRoom'; import { IPaginationOptions, IQueryOptions, IRecordsWithTotal, ITeam, ITeamMember, ITeamStats, TEAM_TYPE } from '../../../definition/ITeam'; import { IUser } from '../../../definition/IUser'; +import { Messages } from '../../../app/models/server'; import { Room, Authorization } from '../../sdk'; import { IListRoomsFilter, @@ -125,6 +126,7 @@ export class TeamService extends ServiceClassInternal implements ITeamService { let roomId = room.id; if (roomId) { await this.RoomsModel.setTeamMainById(roomId, teamId); + Messages.createUserConvertChannelToTeamWithRoomIdAndUser(roomId, team.name, createdBy); } else { const roomType: IRoom['t'] = team.type === TEAM_TYPE.PRIVATE ? 'p' : 'c'; @@ -342,7 +344,7 @@ export class TeamService extends ServiceClassInternal implements ITeamService { throw new Error('missing-userId'); } - const team = await this.TeamModel.findOneById(teamId, { projection: { _id: 1 } }); + const team = await this.TeamModel.findOneById>(teamId, { projection: { _id: 1, roomId: 1 } }); if (!team) { throw new Error('invalid-team'); } @@ -373,6 +375,8 @@ export class TeamService extends ServiceClassInternal implements ITeamService { throw new Error('error-no-owner-channel'); } + Messages.createUserAddRoomToTeamWithRoomIdAndUser(team.roomId, room.name, user); + room.teamId = teamId; } @@ -396,15 +400,15 @@ export class TeamService extends ServiceClassInternal implements ITeamService { throw new Error('invalid-room'); } + const user = await this.Users.findOneById(uid); if (!canRemoveAnyRoom) { - const user = await this.Users.findOneById(uid); const canSeeRoom = await canAccessRoom(room, user); if (!canSeeRoom) { throw new Error('invalid-room'); } } - const team = await this.TeamModel.findOneById(teamId, { projection: { _id: 1 } }); + const team = await this.TeamModel.findOneById>(teamId, { projection: { _id: 1, roomId: 1 } }); if (!team) { throw new Error('invalid-team'); } @@ -416,16 +420,33 @@ export class TeamService extends ServiceClassInternal implements ITeamService { delete room.teamId; delete room.teamDefault; this.RoomsModel.unsetTeamById(room._id); + + Messages.createUserRemoveRoomFromTeamWithRoomIdAndUser(team.roomId, room.name, user); + return { ...room, }; } - async unsetTeamIdOfRooms(teamId: string): Promise { + async unsetTeamIdOfRooms(uid: string, teamId: string): Promise { if (!teamId) { throw new Error('missing-teamId'); } + const team = await this.TeamModel.findOneById>(teamId, { projection: { roomId: 1 } }); + if (!team) { + throw new Error('invalid-team'); + } + + const room = await this.RoomsModel.findOneById>(team.roomId, { projection: { name: 1 } }); + if (!room) { + throw new Error('invalid-room'); + } + + const user = await this.Users.findOneById(uid); + + Messages.createUserConvertTeamToChannelWithRoomIdAndUser(team.roomId, room.name, user); + await this.RoomsModel.unsetTeamId(teamId); } From 906145dd23f41021c6edb08818f10c31db866c77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Feb 2022 09:12:07 -0300 Subject: [PATCH 19/37] Bump pm2 from 5.1.2 to 5.2.0 in /ee/server/services (#24537) Bumps [pm2](https://github.com/Unitech/pm2) from 5.1.2 to 5.2.0. - [Release notes](https://github.com/Unitech/pm2/releases) - [Changelog](https://github.com/Unitech/pm2/blob/master/CHANGELOG.md) - [Commits](https://github.com/Unitech/pm2/compare/5.1.2...5.2.0) --- updated-dependencies: - dependency-name: pm2 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ee/server/services/package-lock.json | 178 +++++++++++---------------- ee/server/services/package.json | 2 +- 2 files changed, 74 insertions(+), 106 deletions(-) diff --git a/ee/server/services/package-lock.json b/ee/server/services/package-lock.json index fe1903a81b9c..abe75fa473d6 100644 --- a/ee/server/services/package-lock.json +++ b/ee/server/services/package-lock.json @@ -651,9 +651,9 @@ } }, "async": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", - "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", + "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", "dev": true }, "async-listener": { @@ -820,9 +820,9 @@ } }, "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, "bufrw": { @@ -863,9 +863,9 @@ "dev": true }, "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -993,14 +993,11 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, - "cron": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/cron/-/cron-1.8.2.tgz", - "integrity": "sha512-Gk2c4y6xKEO8FSAUTklqtfSr7oTq0CiPQeLBG5Fl0qoXpZyMcj1SG59YL+hqq04bu6/IuEA7lMkYDAplQNKkyg==", - "dev": true, - "requires": { - "moment-timezone": "^0.5.x" - } + "croner": { + "version": "4.1.97", + "resolved": "https://registry.npmjs.org/croner/-/croner-4.1.97.tgz", + "integrity": "sha512-/f6gpQuxDaqXu+1kwQYSckUglPaOrHdbIlBAu0YuW8/Cdb45XwXYNUBXg3r/9Mo6n540Kn/smKcZWko5x99KrQ==", + "dev": true }, "culvert": { "version": "0.1.2", @@ -1041,15 +1038,15 @@ "dev": true }, "degenerator": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-3.0.1.tgz", - "integrity": "sha512-LFsIFEeLPlKvAKXu7j3ssIG6RT0TbI7/GhsqrI0DnHASEQjXQ0LUSYcjJteGgRGmZbl1TnMSxpNQIAiJ7Du5TQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-3.0.2.tgz", + "integrity": "sha512-c0mef3SNQo56t6urUU6tdQAs+ThoD0o9B9MJ8HEt7NQcGEILCRFqQb7ZbP9JAv+QF1Ky5plydhMR/IrqWDm+TQ==", "dev": true, "requires": { "ast-types": "^0.13.2", "escodegen": "^1.8.1", "esprima": "^4.0.0", - "vm2": "^3.9.3" + "vm2": "^3.9.8" } }, "delegates": { @@ -1509,9 +1506,9 @@ } }, "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, "has": { @@ -1631,9 +1628,9 @@ } }, "is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", "dev": true, "requires": { "has": "^1.0.3" @@ -1987,21 +1984,6 @@ } } }, - "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", - "dev": true - }, - "moment-timezone": { - "version": "0.5.34", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.34.tgz", - "integrity": "sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==", - "dev": true, - "requires": { - "moment": ">= 2.9.0" - } - }, "mongodb": { "version": "3.7.3", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.3.tgz", @@ -2266,15 +2248,15 @@ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pidusage": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.21.tgz", - "integrity": "sha512-cv3xAQos+pugVX+BfXpHsbyz/dLzX+lr44zNMsYiGxUw+kV5sgQCIcLd1z+0vq+KyC7dJ+/ts2PsfgWfSC3WXA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-3.0.0.tgz", + "integrity": "sha512-8VJLToXhj+RYZGNVw8oxc7dS54iCQXUJ+MDFHezQ/fwF5B8W4OWodAMboc1wb08S/4LiHwAmkT4ohf/d3YPPsw==", "dev": true, "requires": { "safe-buffer": "^5.2.1" @@ -2351,9 +2333,9 @@ "integrity": "sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==" }, "pm2": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/pm2/-/pm2-5.1.2.tgz", - "integrity": "sha512-2nJQeCWjkN0WnTkWctaoZpqrJTiUN/Icw76IMVHHzPhr/p7yQYlEQgHzlL5IFWxO2N1HdBNXNdZft2p4HUmUcA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/pm2/-/pm2-5.2.0.tgz", + "integrity": "sha512-PO5hMVhQ85cTszFM++6v07Me9hPJMkFbHjkFigtMMk+La8ty2wCi2dlBTeZYJDhPUSjK8Ccltpq2buNRcyMOTw==", "dev": true, "requires": { "@pm2/agent": "~2.0.0", @@ -2366,7 +2348,7 @@ "chokidar": "^3.5.1", "cli-tableau": "^2.0.0", "commander": "2.15.1", - "cron": "1.8.2", + "croner": "~4.1.92", "dayjs": "~1.8.25", "debug": "^4.3.1", "enquirer": "2.3.6", @@ -2374,7 +2356,7 @@ "fclone": "1.0.11", "mkdirp": "1.0.4", "needle": "2.4.0", - "pidusage": "2.0.21", + "pidusage": "~3.0", "pm2-axon": "~4.0.1", "pm2-axon-rpc": "~0.7.1", "pm2-deploy": "~1.0.2", @@ -2448,6 +2430,18 @@ "pidusage": "^2.0.21", "systeminformation": "^5.7", "tx2": "~1.0.4" + }, + "dependencies": { + "pidusage": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.21.tgz", + "integrity": "sha512-cv3xAQos+pugVX+BfXpHsbyz/dLzX+lr44zNMsYiGxUw+kV5sgQCIcLd1z+0vq+KyC7dJ+/ts2PsfgWfSC3WXA==", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.2.1" + } + } } }, "poly1305-js": { @@ -2557,48 +2551,15 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", - "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", + "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", "dev": true, "requires": { - "bytes": "3.1.1", + "bytes": "3.1.2", "http-errors": "1.8.1", "iconv-lite": "0.4.24", "unpipe": "1.0.0" - }, - "dependencies": { - "bytes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", - "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", - "dev": true - }, - "http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - } - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true - } } }, "read": { @@ -2675,13 +2636,14 @@ } }, "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", "dev": true, "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, "rfdc": { @@ -2847,13 +2809,13 @@ "dev": true }, "socks": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", - "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", + "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", "dev": true, "requires": { "ip": "^1.1.5", - "smart-buffer": "^4.1.0" + "smart-buffer": "^4.2.0" } }, "socks-proxy-agent": { @@ -2993,10 +2955,16 @@ "has-flag": "^4.0.0" } }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, "systeminformation": { - "version": "5.9.15", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.9.15.tgz", - "integrity": "sha512-0tUYPXffFEsme8n/iTAMk09jpGgqtaGf46QOx7oFmiON9zDUQCahfSymQaCRr4tsq9BkKolaOzp8nqMVNrKIqQ==", + "version": "5.11.3", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.11.3.tgz", + "integrity": "sha512-sjvlk4SUefhwrONUeLijXt+NQyptAiqShd5v6bFJFNr9EVJUr3YSnNxDqCz0gp5EJBUj88pL1ssc8ZHPtngBOw==", "dev": true, "optional": true }, @@ -3219,9 +3187,9 @@ } }, "vm2": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.7.tgz", - "integrity": "sha512-g/GZ7V0Mlmch3eDVOATvAXr1GsJNg6kQ5PjvYy3HbJMCRn5slNbo/u73Uy7r5yUej1cRa3ZjtoVwcWSQuQ/fow==", + "version": "3.9.8", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.8.tgz", + "integrity": "sha512-/1PYg/BwdKzMPo8maOZ0heT7DLI0DAFTm7YQaz/Lim9oIaFZsJs3EdtalvXuBfZwczNwsYhju75NW4d6E+4q+w==", "dev": true, "requires": { "acorn": "^8.7.0", diff --git a/ee/server/services/package.json b/ee/server/services/package.json index e6f452bed5f5..3107190d7d7c 100644 --- a/ee/server/services/package.json +++ b/ee/server/services/package.json @@ -58,7 +58,7 @@ "@types/node": "^14.17.4", "@types/ws": "^8.2.2", "pino-pretty": "^7.5.0", - "pm2": "^5.1.2", + "pm2": "^5.2.0", "ts-node": "^10.5.0", "typescript": "^4.3.5" }, From ee7dd16dc75bf2a2475c240ab50cc7010ee9391d Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Fri, 18 Feb 2022 14:21:25 -0300 Subject: [PATCH 20/37] [FIX] Skip admin info in setup wizard for servers with admin registered (#24485) Co-authored-by: Tasso Evangelista --- client/contexts/ServerContext/methods.ts | 7 +- client/views/setupWizard/SetupWizardPage.tsx | 3 - .../views/setupWizard/hooks/useParameters.ts | 56 +++----- .../views/setupWizard/hooks/useStepRouting.ts | 19 ++- .../providers/SetupWizardProvider.tsx | 38 +++--- .../steps/OrganizationInfoStep.tsx | 4 +- .../setupWizard/steps/RegisterServerStep.tsx | 35 ++++- .../steps/StandaloneServerStep.tsx | 25 ---- package-lock.json | 125 ++++++++++++++++-- package.json | 2 +- server/methods/getSetupWizardParameters.js | 2 +- 11 files changed, 193 insertions(+), 123 deletions(-) delete mode 100644 client/views/setupWizard/steps/StandaloneServerStep.tsx diff --git a/client/contexts/ServerContext/methods.ts b/client/contexts/ServerContext/methods.ts index 8a5436ce9762..96ff8a15cc35 100644 --- a/client/contexts/ServerContext/methods.ts +++ b/client/contexts/ServerContext/methods.ts @@ -1,6 +1,7 @@ import type { DeleteWriteOpResultObject } from 'mongodb'; import { IRoom } from '../../../definition/IRoom'; +import { ISetting } from '../../../definition/ISetting'; import { IUser } from '../../../definition/IUser'; import { AddWebdavAccountMethod } from './methods/addWebdavAccount'; import { FollowMessageMethod } from './methods/followMessage'; @@ -55,7 +56,11 @@ export type ServerMethods = { 'eraseRoom': (...args: any[]) => any; 'followMessage': FollowMessageMethod; 'getAvatarSuggestion': (...args: any[]) => any; - 'getSetupWizardParameters': (...args: any[]) => any; + 'getSetupWizardParameters': () => { + settings: ISetting[]; + serverAlreadyRegistered: boolean; + hasAdmin: boolean; + }; 'getUsersOfRoom': (...args: any[]) => any; 'hideRoom': (...args: any[]) => any; 'ignoreUser': (...args: any[]) => any; diff --git a/client/views/setupWizard/SetupWizardPage.tsx b/client/views/setupWizard/SetupWizardPage.tsx index 165e9a670625..4c876a07ae03 100644 --- a/client/views/setupWizard/SetupWizardPage.tsx +++ b/client/views/setupWizard/SetupWizardPage.tsx @@ -5,7 +5,6 @@ import AdminInfoStep from './steps/AdminInfoStep'; import CloudAccountConfirmation from './steps/CloudAccountConfirmation'; import OrganizationInfoStep from './steps/OrganizationInfoStep'; import RegisterServerStep from './steps/RegisterServerStep'; -import StandaloneServerStep from './steps/StandaloneServerStep'; const SetupWizardPage = (): ReactElement => { const { currentStep } = useSetupWizardContext(); @@ -18,8 +17,6 @@ const SetupWizardPage = (): ReactElement => { case 3: return ; case 4: - return ; - case 5: return ; default: diff --git a/client/views/setupWizard/hooks/useParameters.ts b/client/views/setupWizard/hooks/useParameters.ts index bf13cb07a236..24ec1d0c0529 100644 --- a/client/views/setupWizard/hooks/useParameters.ts +++ b/client/views/setupWizard/hooks/useParameters.ts @@ -1,48 +1,22 @@ -import { useState, useEffect } from 'react'; +import { useQuery, UseQueryResult } from 'react-query'; import { ISetting } from '../../../../definition/ISetting'; import { useMethod } from '../../../contexts/ServerContext'; -export const useParameters = (): { - loaded: boolean; - settings: Array; - skipCloudRegistration: boolean; -} => { - const [loaded, setLoaded] = useState(false); - const [settings, setSettings] = useState>([]); - const [skipCloudRegistration, setSkipCloudRegistration] = useState(false); - const getSetupWizardParameters = useMethod('getSetupWizardParameters'); - - useEffect(() => { - let mounted = true; - const requestParameters = async (): Promise => { - try { - const { setupWizardSettings = [], serverAlreadyRegistered = false } = (await getSetupWizardParameters()) || {}; - - if (!mounted) { - return; - } - - setLoaded(true); - setSettings(setupWizardSettings); - setSkipCloudRegistration(serverAlreadyRegistered); - } catch (error) { - setLoaded(false); - setSettings([]); - setSkipCloudRegistration(false); - } - }; - - requestParameters(); +type SetupWizardParameters = { + settings: ISetting[]; + serverAlreadyRegistered: boolean; + hasAdmin: boolean; +}; - return (): void => { - mounted = false; - }; - }, [getSetupWizardParameters]); +export const useParameters = (): Exclude, { data: undefined }> => { + const getSetupWizardParameters = useMethod('getSetupWizardParameters'); - return { - loaded, - settings, - skipCloudRegistration, - }; + return useQuery(['setupWizard/parameters'], getSetupWizardParameters, { + initialData: { + settings: [], + serverAlreadyRegistered: false, + hasAdmin: false, + }, + }) as Exclude, { data: undefined }>; }; diff --git a/client/views/setupWizard/hooks/useStepRouting.ts b/client/views/setupWizard/hooks/useStepRouting.ts index e28145c69f51..58d5375dcbba 100644 --- a/client/views/setupWizard/hooks/useStepRouting.ts +++ b/client/views/setupWizard/hooks/useStepRouting.ts @@ -1,16 +1,17 @@ import { useState, useEffect, Dispatch, SetStateAction } from 'react'; +import { useRole } from '../../../contexts/AuthorizationContext'; import { useRouteParameter, useRoute } from '../../../contexts/RouterContext'; -import { useUserId } from '../../../contexts/UserContext'; export const useStepRouting = (): [number, Dispatch>] => { const param = useRouteParameter('step'); - const userId = useUserId(); const setupWizardRoute = useRoute('setup-wizard'); + const hasAdminRole = useRole('admin'); + const initialStep = hasAdminRole ? 2 : 1; const [currentStep, setCurrentStep] = useState(() => { if (!param) { - return 1; + return initialStep; } const step = parseInt(param, 10); @@ -18,18 +19,16 @@ export const useStepRouting = (): [number, Dispatch>] => return step; } - return 1; + return initialStep; }); useEffect(() => { - // if (!userId) { - // setCurrentStep(1); - // } else if (currentStep === 1) { - // setCurrentStep(2); - // } + if (hasAdminRole && currentStep === 1) { + setCurrentStep(2); + } setupWizardRoute.replace({ step: String(currentStep) }); - }, [setupWizardRoute, userId, currentStep]); + }, [setupWizardRoute, currentStep, hasAdminRole]); return [currentStep, setCurrentStep]; }; diff --git a/client/views/setupWizard/providers/SetupWizardProvider.tsx b/client/views/setupWizard/providers/SetupWizardProvider.tsx index 1f9dd061216c..8d1d717ca37f 100644 --- a/client/views/setupWizard/providers/SetupWizardProvider.tsx +++ b/client/views/setupWizard/providers/SetupWizardProvider.tsx @@ -1,15 +1,16 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { Meteor } from 'meteor/meteor'; -import React, { useCallback, useMemo, useState, ReactElement, ContextType, useEffect } from 'react'; +import React, { useCallback, useMemo, useState, ReactElement, ContextType } from 'react'; import { callbacks } from '../../../../lib/callbacks'; import { validateEmail } from '../../../../lib/emailValidator'; +import { useRole } from '../../../contexts/AuthorizationContext'; import { useMethod, useEndpoint } from '../../../contexts/ServerContext'; import { useSessionDispatch } from '../../../contexts/SessionContext'; import { useSettingSetValue, useSettingsDispatch } from '../../../contexts/SettingsContext'; import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext'; import { useTranslation } from '../../../contexts/TranslationContext'; -import { useLoginWithPassword, useUserId } from '../../../contexts/UserContext'; +import { useLoginWithPassword } from '../../../contexts/UserContext'; import { SetupWizardContext } from '../contexts/SetupWizardContext'; import { useParameters } from '../hooks/useParameters'; import { useStepRouting } from '../hooks/useStepRouting'; @@ -37,13 +38,12 @@ type HandleRegisterServer = (params: { email: string; resend?: boolean }) => Pro const SetupWizardProvider = ({ children }: { children: ReactElement }): ReactElement => { const t = useTranslation(); - const userId = useUserId(); + const hasAdminRole = useRole('admin'); const [setupWizardData, setSetupWizardData] = useState['setupWizardData']>(initialData); const [currentStep, setCurrentStep] = useStepRouting(); - const { loaded, settings, skipCloudRegistration } = useParameters(); + const { isSuccess, data } = useParameters(); const dispatchToastMessage = useToastMessageDispatch(); const dispatchSettings = useSettingsDispatch(); - const [maxSteps, setMaxSteps] = useState(4); const setShowSetupWizard = useSettingSetValue('Show_Setup_Wizard'); const registerUser = useMethod('registerUser'); @@ -56,12 +56,6 @@ const SetupWizardProvider = ({ children }: { children: ReactElement }): ReactEle const goToNextStep = useCallback(() => setCurrentStep((currentStep) => currentStep + 1), [setCurrentStep]); const goToStep = useCallback((step) => setCurrentStep(() => step), [setCurrentStep]); - useEffect(() => { - if (skipCloudRegistration) { - return setMaxSteps(2); - } - }, [skipCloudRegistration]); - const _validateEmail = useCallback( (email: string): true | string => { if (!validateEmail(email)) { @@ -159,7 +153,7 @@ const SetupWizardProvider = ({ children }: { children: ReactElement }): ReactEle }, [dispatchSettings, setupWizardData]); const registerServer: HandleRegisterServer = useMutableCallback(async ({ email, resend = false }): Promise => { - if (!userId) { + if (!hasAdminRole) { try { await registerAdminUser(); } catch (e) { @@ -180,7 +174,7 @@ const SetupWizardProvider = ({ children }: { children: ReactElement }): ReactEle registrationData: { ...intentData, cloudEmail: email }, })); - goToStep(5); + goToStep(4); setShowSetupWizard('in_progress'); } catch (e) { console.log(e); @@ -188,7 +182,9 @@ const SetupWizardProvider = ({ children }: { children: ReactElement }): ReactEle }); const completeSetupWizard = useMutableCallback(async (): Promise => { - await registerAdminUser(); + if (!hasAdminRole) { + await registerAdminUser(); + } await saveOrganizationData(); dispatchToastMessage({ type: 'success', message: t('Your_workspace_is_ready') }); return setShowSetupWizard('completed'); @@ -199,9 +195,9 @@ const SetupWizardProvider = ({ children }: { children: ReactElement }): ReactEle setupWizardData, setSetupWizardData, currentStep, - loaded, - settings, - skipCloudRegistration, + loaded: isSuccess, + settings: data.settings, + skipCloudRegistration: data.serverAlreadyRegistered, goToPreviousStep, goToNextStep, goToStep, @@ -211,16 +207,15 @@ const SetupWizardProvider = ({ children }: { children: ReactElement }): ReactEle saveWorkspaceData, saveOrganizationData, completeSetupWizard, - maxSteps, + maxSteps: data.serverAlreadyRegistered ? 2 : 3, }), [ setupWizardData, setSetupWizardData, currentStep, - loaded, + isSuccess, registerAdminUser, - settings, - skipCloudRegistration, + data, goToPreviousStep, goToNextStep, goToStep, @@ -229,7 +224,6 @@ const SetupWizardProvider = ({ children }: { children: ReactElement }): ReactEle saveWorkspaceData, saveOrganizationData, completeSetupWizard, - maxSteps, ], ); diff --git a/client/views/setupWizard/steps/OrganizationInfoStep.tsx b/client/views/setupWizard/steps/OrganizationInfoStep.tsx index 6158f3a2c56a..93caba395ee5 100644 --- a/client/views/setupWizard/steps/OrganizationInfoStep.tsx +++ b/client/views/setupWizard/steps/OrganizationInfoStep.tsx @@ -2,6 +2,7 @@ import { OrganizationInfoPage } from '@rocket.chat/onboarding-ui'; import React, { ComponentProps, ReactElement } from 'react'; import { ISetting } from '../../../../definition/ISetting'; +import { useRole } from '../../../contexts/AuthorizationContext'; import { useTranslation, TranslationKey } from '../../../contexts/TranslationContext'; import { useSetupWizardContext } from '../contexts/SetupWizardContext'; @@ -25,6 +26,7 @@ const getSettingOptions = ( const OrganizationInfoStep = (): ReactElement => { const t = useTranslation(); + const hasAdminRole = useRole('admin'); const { setupWizardData: { organizationData }, @@ -55,7 +57,7 @@ const OrganizationInfoStep = (): ReactElement => { { const { goToPreviousStep, - goToNextStep, currentStep, setSetupWizardData, setupWizardData: { adminData }, registerServer, maxSteps, + completeSetupWizard, } = useSetupWizardContext(); + const [serverOption, setServerOption] = useState(SERVER_OPTIONS.REGISTERED); - const handleSubmit: ComponentProps['onSubmit'] = async (data) => { + const handleRegister: ComponentProps['onSubmit'] = async (data) => { if (data.registerType !== 'standalone') { setSetupWizardData((prevState) => ({ ...prevState, serverData: data })); await registerServer(data); } }; + const handleConfirmStandalone: ComponentProps['onSubmit'] = async ({ registerType }) => { + if (registerType !== 'registered') { + return completeSetupWizard(); + } + }; + + if (serverOption === SERVER_OPTIONS.STANDALONE) { + return ( + setServerOption(SERVER_OPTIONS.REGISTERED)} + onSubmit={handleConfirmStandalone} + stepCount={maxSteps} + /> + ); + } + return ( setServerOption(SERVER_OPTIONS.STANDALONE)} onBackButtonClick={goToPreviousStep} stepCount={maxSteps} - onSubmit={handleSubmit} + onSubmit={handleRegister} currentStep={currentStep} initialValues={{ email: adminData.companyEmail }} /> diff --git a/client/views/setupWizard/steps/StandaloneServerStep.tsx b/client/views/setupWizard/steps/StandaloneServerStep.tsx deleted file mode 100644 index b46174bb391a..000000000000 --- a/client/views/setupWizard/steps/StandaloneServerStep.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { StandaloneServerPage } from '@rocket.chat/onboarding-ui'; -import React, { ReactElement, ComponentProps } from 'react'; - -import { useSetupWizardContext } from '../contexts/SetupWizardContext'; - -const CloudAccountStep = (): ReactElement => { - const { goToPreviousStep, currentStep, completeSetupWizard, maxSteps } = useSetupWizardContext(); - - const handleConfirmStandalone: ComponentProps['onSubmit'] = async ({ registerType }) => { - if (registerType !== 'registered') { - return completeSetupWizard(); - } - }; - - return ( - - ); -}; - -export default CloudAccountStep; diff --git a/package-lock.json b/package-lock.json index 9eb2fb47c647..8bb5c362d7c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5588,21 +5588,121 @@ } }, "@rocket.chat/onboarding-ui": { - "version": "0.31.3", - "resolved": "https://registry.npmjs.org/@rocket.chat/onboarding-ui/-/onboarding-ui-0.31.3.tgz", - "integrity": "sha512-CkWKD6tHm6oyNH0u3eypYjegFvzT5ZGt+kF59qagoDpUnXatJ0caJafnQHNN41tRMOoUXMWvZs5yfpXyoswDIg==", + "version": "0.6.3-dev.431", + "resolved": "https://registry.npmjs.org/@rocket.chat/onboarding-ui/-/onboarding-ui-0.6.3-dev.431.tgz", + "integrity": "sha512-fA5FdIisopH2O4QGs/nKmTxKj8VoHHwl7oKuSaalXrX6MOxwcgumjRAOxxypFTRq+jpLz0OT9Mv+caYyg7bS8w==", "requires": { - "@rocket.chat/fuselage": "^0.31.3", - "@rocket.chat/fuselage-hooks": "^0.31.3", - "@rocket.chat/icons": "^0.31.3", - "@rocket.chat/logo": "^0.31.3", - "@rocket.chat/styled": "^0.31.3", - "i18next": "^20.3.2", - "react-hook-form": "^7.10.1", - "react-i18next": "^11.11.0", - "tslib": "^2.3.1" + "@rocket.chat/fuselage": "^0.6.3-dev.431+d3bbf85d", + "@rocket.chat/fuselage-hooks": "^0.6.3-dev.431+d3bbf85d", + "@rocket.chat/icons": "^0.6.3-dev.431+d3bbf85d", + "@rocket.chat/logo": "^0.6.3-dev.431+d3bbf85d", + "@rocket.chat/styled": "^0.6.3-dev.431+d3bbf85d", + "i18next": "~21.6.11", + "react-hook-form": "~7.27.0", + "react-i18next": "~11.15.4", + "tslib": "~2.3.1" }, "dependencies": { + "@rocket.chat/css-in-js": { + "version": "0.6.3-dev.431", + "resolved": "https://registry.npmjs.org/@rocket.chat/css-in-js/-/css-in-js-0.6.3-dev.431.tgz", + "integrity": "sha512-wsjUz52mTMfZXl1Trx9uxpl7lrFNxO4lLlfdXyOmuvbfE9xgKbqBKXBhN6IZjgCoVRoYwtXSd+1k2SiXx2/vJw==", + "requires": { + "@emotion/hash": "^0.8.0", + "@rocket.chat/css-supports": "^0.6.3-dev.431+d3bbf85d", + "@rocket.chat/memo": "^0.6.3-dev.431+d3bbf85d", + "@rocket.chat/stylis-logical-props-middleware": "^0.6.3-dev.431+d3bbf85d", + "stylis": "~4.0.13" + } + }, + "@rocket.chat/css-supports": { + "version": "0.6.3-dev.431", + "resolved": "https://registry.npmjs.org/@rocket.chat/css-supports/-/css-supports-0.6.3-dev.431.tgz", + "integrity": "sha512-Xuus3OAWQXaWc3kvKTz8vh7/FVsNm05zK/dH6iH+o0XDqXmh35/jrR8dbeoA7uOIMfnreLrWW/h88tbn4HahLA==", + "requires": { + "@rocket.chat/memo": "^0.6.3-dev.431+d3bbf85d" + } + }, + "@rocket.chat/fuselage": { + "version": "0.6.3-dev.431", + "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage/-/fuselage-0.6.3-dev.431.tgz", + "integrity": "sha512-1tRER6zctLHQrhalt8e2YMXuCA53bzlsNoqQEmDjHhZB5dSDYZs6Fn+iodu/iek+NzEZi4fXlB/VPKcfSxdT5Q==", + "requires": { + "@rocket.chat/css-in-js": "^0.6.3-dev.431+d3bbf85d", + "@rocket.chat/css-supports": "^0.6.3-dev.431+d3bbf85d", + "@rocket.chat/fuselage-tokens": "^0.6.3-dev.431+d3bbf85d", + "@rocket.chat/memo": "^0.6.3-dev.431+d3bbf85d", + "invariant": "^2.2.4", + "react-keyed-flatten-children": "^1.3.0" + } + }, + "@rocket.chat/fuselage-hooks": { + "version": "0.6.3-dev.431", + "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-hooks/-/fuselage-hooks-0.6.3-dev.431.tgz", + "integrity": "sha512-OJFzZ8rcKZLXUL8slr5ybmSuGBXo3yWDqY2fjvprDagqCScYHsvr4semVGaCT6QwTIClcZSLJOMM+Nunpp9beQ==", + "requires": { + "@testing-library/user-event": "^13.5.0" + } + }, + "@rocket.chat/fuselage-tokens": { + "version": "0.6.3-dev.431", + "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-tokens/-/fuselage-tokens-0.6.3-dev.431.tgz", + "integrity": "sha512-pySxIPVX6CYqxpRhzd6JlJKOon3lNfQqdzls6PherU35c237UmIQpcJxUB8R4j8WqtrjrY5/Cnw/D6W16iPxsQ==" + }, + "@rocket.chat/icons": { + "version": "0.6.3-dev.431", + "resolved": "https://registry.npmjs.org/@rocket.chat/icons/-/icons-0.6.3-dev.431.tgz", + "integrity": "sha512-aVb8oKKFtl30d1hxp1jxduKNN++bP0gsvCriXWM6PqX0VH9Pa8lHQ7nlVeO2xsBHwGKr3dqwkQLSSZiEJxyMEQ==" + }, + "@rocket.chat/logo": { + "version": "0.6.3-dev.431", + "resolved": "https://registry.npmjs.org/@rocket.chat/logo/-/logo-0.6.3-dev.431.tgz", + "integrity": "sha512-E1cSLj6+3y1r+Z7nh7vIIny0edDZZg855XRT0u6O9B2kooQg3tb8KsxA3NfmpQZfFc8IQ7uwGpjowbabtnY4ZA==", + "requires": { + "@rocket.chat/fuselage-hooks": "^0.6.3-dev.431+d3bbf85d", + "@rocket.chat/styled": "^0.6.3-dev.431+d3bbf85d", + "tslib": "^2.3.1" + } + }, + "@rocket.chat/memo": { + "version": "0.6.3-dev.431", + "resolved": "https://registry.npmjs.org/@rocket.chat/memo/-/memo-0.6.3-dev.431.tgz", + "integrity": "sha512-GVfcJz738XbqsOQcDFQt/OLD0QYcHtKv7wwSAwqRbUvrz9OZ4elJZauNzkgDeq5w3ZGn/6oo9dCajrHYRlDVvQ==", + "requires": { + "tslib": "^2.3.1" + } + }, + "@rocket.chat/styled": { + "version": "0.6.3-dev.431", + "resolved": "https://registry.npmjs.org/@rocket.chat/styled/-/styled-0.6.3-dev.431.tgz", + "integrity": "sha512-3gd9Q/P0MZ/6GJMzuGH14GQNO5lWo/pWAfbpfCogG2GhtuJZiN33VR+vzbzlTbjCc7TboorqOFByOXeyPz+aLQ==", + "requires": { + "@rocket.chat/css-in-js": "^0.6.3-dev.431+d3bbf85d", + "tslib": "^2.3.1" + } + }, + "@rocket.chat/stylis-logical-props-middleware": { + "version": "0.6.3-dev.431", + "resolved": "https://registry.npmjs.org/@rocket.chat/stylis-logical-props-middleware/-/stylis-logical-props-middleware-0.6.3-dev.431.tgz", + "integrity": "sha512-uElC6eWkXzW8MHAhrR/GWmyTbMCGW+3l4X2+oQq/CyUYS7AjBGmczXFJYu+l2a+TRhlP6YZLb22TCTu+g7bEvA==", + "requires": { + "@rocket.chat/css-supports": "^0.6.3-dev.431+d3bbf85d", + "tslib": "^2.3.1" + } + }, + "i18next": { + "version": "21.6.11", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-21.6.11.tgz", + "integrity": "sha512-tJ2+o0lVO+fhi8bPkCpBAeY1SgkqmQm5NzgPWCQssBrywJw98/o+Kombhty5nxQOpHtvMmsxcOopczUiH6bJxQ==", + "requires": { + "@babel/runtime": "^7.12.0" + } + }, + "react-hook-form": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.27.0.tgz", + "integrity": "sha512-NEh3Qbz1Rg3w95SRZv0kHorHN3frtMKasplznMBr8RkFrE4pVxjd/zo3clnFXpD0FppUVHBMfsTMtTsa6wyQrA==" + }, "tslib": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", @@ -22454,6 +22554,7 @@ "version": "20.3.2", "resolved": "https://registry.npmjs.org/i18next/-/i18next-20.3.2.tgz", "integrity": "sha512-e8CML2R9Ng2sSQOM80wb/PrM2j8mDm84o/T4Amzn9ArVyNX5/ENWxxAXkRpZdTQNDaxKImF93Wep4mAoozFrKw==", + "dev": true, "requires": { "@babel/runtime": "^7.12.0" } diff --git a/package.json b/package.json index 8caf9df6e2e9..4ea0370d161d 100644 --- a/package.json +++ b/package.json @@ -189,7 +189,7 @@ "@rocket.chat/memo": "^0.31.3", "@rocket.chat/message-parser": "^0.31.3", "@rocket.chat/mp3-encoder": "^0.24.0", - "@rocket.chat/onboarding-ui": "^0.31.3", + "@rocket.chat/onboarding-ui": "^0.6.3-dev.431", "@rocket.chat/string-helpers": "^0.31.3", "@rocket.chat/ui-kit": "^0.31.3", "@slack/client": "^4.12.0", diff --git a/server/methods/getSetupWizardParameters.js b/server/methods/getSetupWizardParameters.js index 69993359c70a..2655e8af7b41 100644 --- a/server/methods/getSetupWizardParameters.js +++ b/server/methods/getSetupWizardParameters.js @@ -9,7 +9,7 @@ Meteor.methods({ const serverAlreadyRegistered = !!settings.get('Cloud_Workspace_Client_Id') || process.env.DEPLOY_PLATFORM === 'rocket-cloud'; return { - setupWizardSettings, + settings: setupWizardSettings, serverAlreadyRegistered, }; }, From a6a04cb2b488a26dbc9206223a72c88886b71bd6 Mon Sep 17 00:00:00 2001 From: Debdut Chakraborty Date: Sat, 19 Feb 2022 01:27:44 +0530 Subject: [PATCH 21/37] [FIX] respect `Accounts_Registration_Users_Default_Roles` setting (#24173) * Fix role handling if setup wizard pending and no admin found Signed-off-by: Debdut Chakraborty * [FIX] Create `findOneByRolesAndType` function (#24308) * Create findOneByRolesAndType function and stop adding user role by default * Remove logs * Remove default user role in userHandler.js * Update app/authentication/server/startup/index.js Co-authored-by: Debdut Chakraborty * fix lint issues * Check if the server has an admin user before assigning the admin role on server startup Co-authored-by: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> Co-authored-by: matheusbsilva137 --- app/authentication/server/startup/index.js | 36 ++++++++-------------- app/models/server/models/Users.js | 6 ++++ 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/app/authentication/server/startup/index.js b/app/authentication/server/startup/index.js index eb0ed5415673..6f3c439f7098 100644 --- a/app/authentication/server/startup/index.js +++ b/app/authentication/server/startup/index.js @@ -8,7 +8,7 @@ import { escapeRegExp, escapeHTML } from '@rocket.chat/string-helpers'; import * as Mailer from '../../../mailer/server/api'; import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; -import { Users, Settings } from '../../../models/server'; +import { Settings, Users } from '../../../models/server'; import { Roles, Users as UsersRaw } from '../../../models/server/raw'; import { addUserRoles } from '../../../authorization/server'; import { getAvatarSuggestionForUser } from '../../../lib/server/functions/getAvatarSuggestionForUser'; @@ -213,8 +213,6 @@ Accounts.onCreateUser(function (options, user = {}) { }); Accounts.insertUserDoc = _.wrap(Accounts.insertUserDoc, function (insertUserDoc, options, user) { - const noRoles = !user?.hasOwnProperty('globalRoles'); - const globalRoles = []; if (Match.test(user.globalRoles, [String]) && user.globalRoles.length > 0) { @@ -278,26 +276,18 @@ Accounts.insertUserDoc = _.wrap(Accounts.insertUserDoc, function (insertUserDoc, } } - if (noRoles || roles.length === 0) { - const hasAdmin = Users.findOne( - { - roles: 'admin', - type: 'user', - }, - { - fields: { - _id: 1, - }, - }, - ); - - if (hasAdmin) { - roles.push('user'); - } else { - roles.push('admin'); - if (settings.get('Show_Setup_Wizard') === 'pending') { - Settings.updateValueById('Show_Setup_Wizard', 'in_progress'); - } + /** + * if settings shows setup wizard to be pending + * and no admin's been found, + * and existing role list doesn't include admin + * create this user admin. + * count this as the completion of setup wizard step 1. + */ + const hasAdmin = Users.findOneByRolesAndType('admin', 'user', { fields: { _id: 1 } }); + if (!roles.includes('admin') && !hasAdmin) { + roles.push('admin'); + if (settings.get('Show_Setup_Wizard') === 'pending') { + Settings.updateValueById('Show_Setup_Wizard', 'in_progress'); } } diff --git a/app/models/server/models/Users.js b/app/models/server/models/Users.js index 8368c26e3409..01f36bcce90c 100644 --- a/app/models/server/models/Users.js +++ b/app/models/server/models/Users.js @@ -767,6 +767,12 @@ export class Users extends Base { return this.findOne(query, options); } + findOneByRolesAndType(roles, type, options) { + const query = { roles, type }; + + return this.findOne(query, options); + } + // FIND findByIds(users, options) { const query = { _id: { $in: users } }; From 1ff808fd0ed139408253387a9ac57f1020333d45 Mon Sep 17 00:00:00 2001 From: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com> Date: Mon, 21 Feb 2022 17:26:15 +0530 Subject: [PATCH 22/37] [FIX] Room context tabs not working in Omnichannel current chats page (#24559) --- client/views/omnichannel/routes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/views/omnichannel/routes.js b/client/views/omnichannel/routes.js index 71cf9bf912d8..8fd4c1e5fa45 100644 --- a/client/views/omnichannel/routes.js +++ b/client/views/omnichannel/routes.js @@ -62,7 +62,7 @@ registerOmnichannelRoute('/facebook', { lazyRouteComponent: () => import('./facebook/FacebookPageContainer'), }); -registerOmnichannelRoute('/current/:id?', { +registerOmnichannelRoute('/current/:id?/:tab?/:context?', { name: 'omnichannel-current-chats', lazyRouteComponent: () => import('./currentChats/CurrentChatsRoute'), }); From d3a595ffa8438d071c3222a461b3cf9eff009920 Mon Sep 17 00:00:00 2001 From: Renato Becker Date: Mon, 21 Feb 2022 11:04:07 -0300 Subject: [PATCH 23/37] [FIX] Omnichannel managers can't join chats in progress (#24553) Co-authored-by: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com> --- .../client/views/app/livechatReadOnly.html | 5 ++- .../client/views/app/livechatReadOnly.js | 40 +++++++++++++---- app/livechat/lib/LivechatRoomType.js | 10 ++--- app/livechat/server/api/v1/room.js | 38 ++++++++++++++++ app/livechat/server/methods/takeInquiry.js | 4 +- .../client/imports/components/message-box.css | 4 ++ definition/rest/v1/omnichannel.ts | 3 ++ .../server/hooks/beforeJoinRoom.ts | 45 +++++++++++++++++++ .../livechat-enterprise/server/hooks/index.js | 1 + packages/rocketchat-i18n/i18n/en.i18n.json | 8 ++-- packages/rocketchat-i18n/i18n/pt-BR.i18n.json | 4 +- server/modules/listeners/listeners.module.ts | 21 +-------- 12 files changed, 140 insertions(+), 43 deletions(-) create mode 100644 ee/app/livechat-enterprise/server/hooks/beforeJoinRoom.ts diff --git a/app/livechat/client/views/app/livechatReadOnly.html b/app/livechat/client/views/app/livechatReadOnly.html index c2c878a8d29f..efe4e7fc63da 100644 --- a/app/livechat/client/views/app/livechatReadOnly.html +++ b/app/livechat/client/views/app/livechatReadOnly.html @@ -15,7 +15,10 @@ {{else}} - {{_ "room_is_read_only"}} +

+ {{{_ "room_is_read_only"}}} + +
{{/if}} {{/if}} {{/if}} diff --git a/app/livechat/client/views/app/livechatReadOnly.js b/app/livechat/client/views/app/livechatReadOnly.js index ac95a2c347d2..fa38742ca152 100644 --- a/app/livechat/client/views/app/livechatReadOnly.js +++ b/app/livechat/client/views/app/livechatReadOnly.js @@ -7,13 +7,14 @@ import { ChatRoom, CachedChatRoom } from '../../../../models'; import { callWithErrorHandling } from '../../../../../client/lib/utils/callWithErrorHandling'; import './livechatReadOnly.html'; import { APIClient } from '../../../../utils/client'; +import { RoomManager } from '../../../../ui-utils/client/lib/RoomManager'; import { inquiryDataStream } from '../../lib/stream/inquiry'; +import { handleError } from '../../../../../client/lib/utils/handleError'; Template.livechatReadOnly.helpers({ inquiryOpen() { const inquiry = Template.instance().inquiry.get(); - const room = Template.instance().room.get(); - return (inquiry && inquiry.status === 'queued') || !room.servedBy; + return inquiry && inquiry.status === 'queued'; }, roomOpen() { @@ -54,15 +55,29 @@ Template.livechatReadOnly.events({ await callWithErrorHandling('livechat:resumeOnHold', room._id, { clientAction: true }); }, + + async 'click .js-join-it'(event) { + event.preventDefault(); + event.stopPropagation(); + + try { + const { success } = (await APIClient.v1.get(`livechat/room.join?roomId=${this.rid}`)) || {}; + if (!success) { + throw new Meteor.Error('error-join-room', 'Error joining room'); + } + } catch (error) { + handleError(error); + throw error; + } + }, }); -Template.livechatReadOnly.onCreated(function () { +Template.livechatReadOnly.onCreated(async function () { this.rid = Template.currentData().rid; this.room = new ReactiveVar(); this.inquiry = new ReactiveVar(); this.routingConfig = new ReactiveVar({}); this.preparing = new ReactiveVar(true); - this.updateInquiry = async ({ clientAction, ...inquiry }) => { if (clientAction === 'removed') { // this will force to refresh the room @@ -82,20 +97,24 @@ Template.livechatReadOnly.onCreated(function () { } }); - this.loadInquiry = async (roomId) => { + this.loadRoomAndInquiry = async (roomId) => { this.preparing.set(true); const { inquiry } = await APIClient.v1.get(`livechat/inquiries.getOne?roomId=${roomId}`); this.inquiry.set(inquiry); if (inquiry && inquiry._id) { inquiryDataStream.on(inquiry._id, this.updateInquiry); } + + const { room } = await APIClient.v1.get(`rooms.info?roomId=${roomId}`); + this.room.set(room); + if (room && room._id) { + RoomManager.roomStream.on(roomId, (room) => this.room.set(room)); + } + this.preparing.set(false); }; - this.autorun(() => this.loadInquiry(this.rid)); - this.autorun(() => { - this.room.set(ChatRoom.findOne({ _id: Template.currentData().rid }, { fields: { open: 1, servedBy: 1 } })); - }); + this.autorun(() => this.loadRoomAndInquiry(this.rid)); }); Template.livechatReadOnly.onDestroyed(function () { @@ -103,4 +122,7 @@ Template.livechatReadOnly.onDestroyed(function () { if (inquiry && inquiry._id) { inquiryDataStream.removeListener(inquiry._id, this.updateInquiry); } + + const { rid } = Template.currentData(); + RoomManager.roomStream.removeListener(rid); }); diff --git a/app/livechat/lib/LivechatRoomType.js b/app/livechat/lib/LivechatRoomType.js index 2ce5f2328a3e..ed15896a4221 100644 --- a/app/livechat/lib/LivechatRoomType.js +++ b/app/livechat/lib/LivechatRoomType.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { Session } from 'meteor/session'; -import { ChatRoom } from '../../models'; +import { ChatRoom, ChatSubscription } from '../../models'; import { settings } from '../../settings'; import { hasPermission } from '../../authorization'; import { openRoom } from '../../ui-utils'; @@ -106,12 +106,8 @@ export default class LivechatRoomType extends RoomTypeConfig { return true; } - const inquiry = LivechatInquiry.findOne({ rid }, { fields: { status: 1 } }); - if (inquiry && inquiry.status === 'queued') { - return true; - } - - return !room.servedBy; + const subscription = ChatSubscription.findOne({ rid }); + return !subscription; } getAvatarPath(roomData) { diff --git a/app/livechat/server/api/v1/room.js b/app/livechat/server/api/v1/room.js index d29dbb6d5002..4d14f9977676 100644 --- a/app/livechat/server/api/v1/room.js +++ b/app/livechat/server/api/v1/room.js @@ -11,6 +11,8 @@ import { Livechat } from '../../lib/Livechat'; import { normalizeTransferredByData } from '../../lib/Helper'; import { findVisitorInfo } from '../lib/visitors'; import { OmnichannelSourceType } from '../../../../../definition/IRoom'; +import { canAccessRoom } from '../../../../authorization/server'; +import { addUserToRoom } from '../../../../lib/server/functions'; API.v1.addRoute('livechat/room', { get() { @@ -243,3 +245,39 @@ API.v1.addRoute( }, }, ); + +API.v1.addRoute( + 'livechat/room.join', + { authRequired: true }, + { + get() { + try { + check(this.queryParams, { roomId: String }); + + const { roomId } = this.queryParams; + + const { user } = this; + + if (!user) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'joinRoom' }); + } + + const room = LivechatRooms.findOneById(roomId); + + if (!room) { + throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'joinRoom' }); + } + + if (!canAccessRoom(room, user)) { + throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'joinRoom' }); + } + + addUserToRoom(roomId, user); + + return API.v1.success(); + } catch (e) { + return API.v1.failure(e); + } + }, + }, +); diff --git a/app/livechat/server/methods/takeInquiry.js b/app/livechat/server/methods/takeInquiry.js index 1759a8c5a63b..f6852ad32c46 100644 --- a/app/livechat/server/methods/takeInquiry.js +++ b/app/livechat/server/methods/takeInquiry.js @@ -16,7 +16,7 @@ Meteor.methods({ const inquiry = LivechatInquiry.findOneById(inquiryId); if (!inquiry || inquiry.status === 'taken') { - throw new Meteor.Error('error-not-allowed', 'Inquiry already taken', { + throw new Meteor.Error('error-inquiry-taken', 'Inquiry already taken', { method: 'livechat:takeInquiry', }); } @@ -25,7 +25,7 @@ Meteor.methods({ fields: { _id: 1, username: 1, roles: 1, status: 1, statusLivechat: 1 }, }); if (!userCanTakeInquiry(user)) { - throw new Meteor.Error('error-not-allowed', 'Not allowed', { + throw new Meteor.Error('error-agent-status-service-offline', 'Agent status is offline or Omnichannel service is not active', { method: 'livechat:takeInquiry', }); } diff --git a/app/theme/client/imports/components/message-box.css b/app/theme/client/imports/components/message-box.css index 649a1e0826e7..92c801d2be82 100644 --- a/app/theme/client/imports/components/message-box.css +++ b/app/theme/client/imports/components/message-box.css @@ -274,6 +274,10 @@ margin: 0 0.5rem; } + &__join-it-button { + margin: 0 0.5rem; + } + &__cannot-send { display: flex; justify-content: space-between; diff --git a/definition/rest/v1/omnichannel.ts b/definition/rest/v1/omnichannel.ts index 075c531820bc..5a5d0644f191 100644 --- a/definition/rest/v1/omnichannel.ts +++ b/definition/rest/v1/omnichannel.ts @@ -30,6 +30,9 @@ export type OmnichannelEndpoints = { 'livechat/room.onHold': { POST: (params: { roomId: IRoom['_id'] }) => void; }; + 'livechat/room.join': { + GET: (params: { roomId: IRoom['_id'] }) => { success: boolean }; + }; 'livechat/monitors.list': { GET: (params: PaginatedRequest<{ text: string }>) => PaginatedResult<{ monitors: ILivechatMonitor[]; diff --git a/ee/app/livechat-enterprise/server/hooks/beforeJoinRoom.ts b/ee/app/livechat-enterprise/server/hooks/beforeJoinRoom.ts new file mode 100644 index 000000000000..487b0548ac86 --- /dev/null +++ b/ee/app/livechat-enterprise/server/hooks/beforeJoinRoom.ts @@ -0,0 +1,45 @@ +import { Meteor } from 'meteor/meteor'; + +import { callbacks } from '../../../../../lib/callbacks'; +import { Users } from '../../../../../app/models/server/raw'; +import { settings } from '../../../../../app/settings/server'; +import { getMaxNumberSimultaneousChat } from '../lib/Helper'; +import { isOmnichannelRoom, IRoom } from '../../../../../definition/IRoom'; +import { IUser } from '../../../../../definition/IUser'; + +callbacks.add( + 'beforeJoinRoom', + (user: IUser, room?: IRoom): IUser => { + if (!settings.get('Livechat_waiting_queue')) { + return user; + } + + if (!room || !isOmnichannelRoom(room)) { + return user; + } + + const { departmentId } = room; + const maxNumberSimultaneousChat = getMaxNumberSimultaneousChat({ + agentId: user._id, + departmentId, + }); + + if (maxNumberSimultaneousChat === 0) { + return user; + } + + const userSubs = Promise.await(Users.getAgentAndAmountOngoingChats(user._id)); + if (!userSubs) { + return user; + } + + const { queueInfo: { chats = 0 } = {} } = userSubs; + if (maxNumberSimultaneousChat <= chats) { + throw new Meteor.Error('error-max-number-simultaneous-chats-reached', 'Not allowed'); + } + + return user; + }, + callbacks.priority.MEDIUM, + 'livechat-before-join-room', +); diff --git a/ee/app/livechat-enterprise/server/hooks/index.js b/ee/app/livechat-enterprise/server/hooks/index.js index 8f370a0f7c57..a5d2e3092278 100644 --- a/ee/app/livechat-enterprise/server/hooks/index.js +++ b/ee/app/livechat-enterprise/server/hooks/index.js @@ -1,6 +1,7 @@ import './addDepartmentAncestors'; import './afterForwardChatToDepartment'; import './beforeListTags'; +import './beforeJoinRoom'; import './setPredictedVisitorAbandonmentTime'; import './beforeForwardRoomToDepartment'; import './afterRemoveDepartment'; diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index af608011732c..bb6cc1f76815 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -1662,6 +1662,7 @@ "Error_Site_URL_description": "Please, update your \"Site_Url\" setting find more information here", "error-action-not-allowed": "__action__ is not allowed", "error-agent-offline": "Agent is offline", + "error-agent-status-service-offline": "Agent status is offline or Omnichannel service is not active", "error-application-not-found": "Application not found", "error-archived-duplicate-name": "There's an archived channel with name '__room_name__'", "error-avatar-invalid-url": "Invalid avatar URL: __url__", @@ -1696,6 +1697,7 @@ "error-import-file-missing": "The file to be imported was not found on the specified path.", "error-importer-not-defined": "The importer was not defined correctly, it is missing the Import class.", "error-input-is-not-a-valid-field": "__input__ is not a valid __field__", + "error-inquiry-taken": "Inquiry already taken", "error-invalid-account": "Invalid Account", "error-invalid-actionlink": "Invalid action link", "error-invalid-arguments": "Invalid arguments", @@ -2942,9 +2944,9 @@ "Message_HideType_ujt": "Hide \"User Joined Team\" messages", "Message_HideType_ul": "Hide \"User Leave\" messages", "Message_HideType_ult": "Hide \"User Left Team\" messages", - "Message_HideType_user_added_room_to_team": "Hide \"User Added Room to Team\" messages", + "Message_HideType_user_added_room_to_team": "Hide \"User Added Room to Team\" messages", "Message_HideType_user_converted_to_channel": "Hide \"User converted team to a Channel\" messages", - "Message_HideType_user_converted_to_team": "Hide \"User converted channel to a Team\" messages", + "Message_HideType_user_converted_to_team": "Hide \"User converted channel to a Team\" messages", "Message_HideType_user_deleted_room_from_team": "Hide \"User deleted room from Team\" messages", "Message_HideType_user_removed_room_from_team": "Hide \"User removed room from Team\" messages", "Message_HideType_ut": "Hide \"User Joined Conversation\" messages", @@ -4875,4 +4877,4 @@ "onboarding.form.standaloneServerForm.servicesUnavailable": "Some of the services will be unavailable or will require manual setup", "onboarding.form.standaloneServerForm.publishOwnApp": "In order to send push notitications you need to compile and publish your own app to Google Play and App Store", "onboarding.form.standaloneServerForm.manuallyIntegrate": "Need to manually integrate with external services" -} \ No newline at end of file +} diff --git a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json index 9a5523ac5490..277ba157673e 100644 --- a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -1664,6 +1664,7 @@ "Error_Site_URL_description": "Por favor, atualize sua configuração \"Site_Url\". Saiba mais aqui", "error-action-not-allowed": "__action__ não é permitido", "error-agent-offline": "Agente está offline", + "error-agent-status-service-offline": "Status do agente está offline ou serviço Omnichannel não está ativado", "error-application-not-found": "Aplicação não encontrada", "error-archived-duplicate-name": "Já há um canal arquivado com o nome '__room_name__'", "error-avatar-invalid-url": "URL inválida de avatar: __url__", @@ -1698,6 +1699,7 @@ "error-import-file-missing": "O arquivo a ser importado não foi encontrado no caminho especificado.", "error-importer-not-defined": "O importador não foi definido corretamente; está faltando a classe Import.", "error-input-is-not-a-valid-field": "__input__ não é válido um __field__", + "error-inquiry-taken": "Chat já está em atendimento", "error-invalid-account": "Conta inválida", "error-invalid-actionlink": "Link de ação inválido", "error-invalid-arguments": "Argumentos inválidos", @@ -4862,4 +4864,4 @@ "onboarding.form.standaloneServerForm.title": "Confirmação de Servidor Standalone", "onboarding.form.standaloneServerForm.servicesUnavailable": "Alguns dos serviços estarão indisponíveis ou vão requerer configuração manual", "onboarding.form.standaloneServerForm.publishOwnApp": "De modo a enviar notificações push você precisará compilar e publicar seu próprio aplicativo no Google Play e App Store" -} \ No newline at end of file +} diff --git a/server/modules/listeners/listeners.module.ts b/server/modules/listeners/listeners.module.ts index aafd5771c268..7767f92a6aca 100644 --- a/server/modules/listeners/listeners.module.ts +++ b/server/modules/listeners/listeners.module.ts @@ -1,7 +1,6 @@ import { IServiceClass } from '../../sdk/types/ServiceClass'; import { NotificationsModule } from '../notifications/notifications.module'; -import { EnterpriseSettings, MeteorService } from '../../sdk/index'; -import { IRoutingManagerConfig } from '../../../definition/IRoutingManagerConfig'; +import { EnterpriseSettings } from '../../sdk/index'; import { UserStatus } from '../../../definition/UserStatus'; import { isSettingColor } from '../../../definition/ISetting'; @@ -137,21 +136,7 @@ export class ListenersModule { notifications.streamRoles.emitWithoutBroadcast('roles', payload); }); - let autoAssignAgent: IRoutingManagerConfig | undefined; - async function getRoutingManagerConfig(): Promise { - if (!autoAssignAgent) { - autoAssignAgent = await MeteorService.getRoutingManagerConfig(); - } - - return autoAssignAgent; - } - service.onEvent('watch.inquiries', async ({ clientAction, inquiry, diff }): Promise => { - const config = await getRoutingManagerConfig(); - if (!config || config.autoAssignAgent) { - return; - } - const type = minimongoChangeMap[clientAction]; if (clientAction === 'removed') { notifications.streamLivechatQueueData.emitWithoutBroadcast(inquiry._id, { @@ -189,10 +174,6 @@ export class ListenersModule { }); service.onEvent('watch.settings', async ({ clientAction, setting }): Promise => { - if (setting._id === 'Livechat_Routing_Method') { - autoAssignAgent = undefined; - } - if (clientAction !== 'removed') { const result = await EnterpriseSettings.changeSettingValue(setting); if (result !== undefined && !(result instanceof Error)) { From 0706212903e716f957f4dab04e89cd04c29f9c8c Mon Sep 17 00:00:00 2001 From: Leonardo Ostjen Couto Date: Mon, 21 Feb 2022 12:04:10 -0300 Subject: [PATCH 24/37] [NEW] E2E password generator (#24114) * created new pwd scheme * added separator to generatMnemonicPhrase * cryptographically secure pseudo-random generator * JUst test * Changed the module crypto name to crypto_module * Use `Random.fraction()` as random generator Co-authored-by: Eduardo Cabrera Co-authored-by: Tasso Evangelista --- app/e2e/client/helper.js | 16 + app/e2e/client/rocketchat.e2e.js | 8 +- app/e2e/client/wordList.ts | 1635 ++++++++++++++++++++++++++++++ lib/random.ts | 7 + 4 files changed, 1662 insertions(+), 4 deletions(-) create mode 100644 app/e2e/client/wordList.ts diff --git a/app/e2e/client/helper.js b/app/e2e/client/helper.js index d2c40ecd626b..8aec8db794d3 100644 --- a/app/e2e/client/helper.js +++ b/app/e2e/client/helper.js @@ -2,6 +2,8 @@ import ByteBuffer from 'bytebuffer'; +import { getRandomFraction } from '../../../lib/random'; + const StaticArrayBufferProto = new ArrayBuffer().__proto__; export function toString(thing) { @@ -122,6 +124,20 @@ export async function readFileAsArrayBuffer(file) { }); } +export async function generateMnemonicPhrase(n, sep = ' ') { + const { default: wordList } = await import('./wordList'); + const result = new Array(n); + let len = wordList.length; + const taken = new Array(len); + + while (n--) { + const x = Math.floor(getRandomFraction() * len); + result[n] = wordList[x in taken ? taken[x] : x]; + taken[x] = --len in taken ? taken[len] : len; + } + return result.join(sep); +} + export class Deferred { constructor() { const p = new Promise((resolve, reject) => { diff --git a/app/e2e/client/rocketchat.e2e.js b/app/e2e/client/rocketchat.e2e.js index 913797f53f4a..88935931b832 100644 --- a/app/e2e/client/rocketchat.e2e.js +++ b/app/e2e/client/rocketchat.e2e.js @@ -1,5 +1,4 @@ import { Meteor } from 'meteor/meteor'; -import { Random } from 'meteor/random'; import { ReactiveVar } from 'meteor/reactive-var'; import { EJSON } from 'meteor/ejson'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; @@ -18,6 +17,7 @@ import { importRSAKey, importRawKey, deriveKey, + generateMnemonicPhrase, } from './helper'; import * as banners from '../../../client/lib/banners'; import { Rooms, Subscriptions, Messages } from '../../models/client'; @@ -136,7 +136,7 @@ class E2E extends Emitter { if (!this.db_public_key || !this.db_private_key) { await call('e2e.setUserPublicAndPrivateKeys', { public_key: Meteor._localStorage.getItem('public_key'), - private_key: await this.encodePrivateKey(Meteor._localStorage.getItem('private_key'), this.createRandomPassword()), + private_key: await this.encodePrivateKey(Meteor._localStorage.getItem('private_key'), await this.createRandomPassword()), }); } @@ -256,8 +256,8 @@ class E2E extends Emitter { call('e2e.requestSubscriptionKeys'); } - createRandomPassword() { - const randomPassword = `${Random.id(3)}-${Random.id(3)}-${Random.id(3)}`.toLowerCase(); + async createRandomPassword() { + const randomPassword = await generateMnemonicPhrase(5); Meteor._localStorage.setItem('e2e.randomPassword', randomPassword); return randomPassword; } diff --git a/app/e2e/client/wordList.ts b/app/e2e/client/wordList.ts new file mode 100644 index 000000000000..f2930b6cb969 --- /dev/null +++ b/app/e2e/client/wordList.ts @@ -0,0 +1,1635 @@ +export default [ + 'acrobat', + 'africa', + 'alaska', + 'albert', + 'albino', + 'album', + 'alcohol', + 'alex', + 'alpha', + 'amadeus', + 'amanda', + 'amazon', + 'america', + 'analog', + 'animal', + 'antenna', + 'antonio', + 'apollo', + 'april', + 'aroma', + 'artist', + 'aspirin', + 'athlete', + 'atlas', + 'banana', + 'bandit', + 'banjo', + 'bikini', + 'bingo', + 'bonus', + 'camera', + 'canada', + 'carbon', + 'casino', + 'catalog', + 'cinema', + 'citizen', + 'cobra', + 'comet', + 'compact', + 'complex', + 'context', + 'credit', + 'critic', + 'crystal', + 'culture', + 'david', + 'delta', + 'dialog', + 'diploma', + 'doctor', + 'domino', + 'dragon', + 'drama', + 'extra', + 'fabric', + 'final', + 'focus', + 'forum', + 'galaxy', + 'gallery', + 'global', + 'harmony', + 'hotel', + 'humor', + 'index', + 'japan', + 'kilo', + 'lemon', + 'liter', + 'lotus', + 'mango', + 'melon', + 'menu', + 'meter', + 'metro', + 'mineral', + 'model', + 'music', + 'object', + 'piano', + 'pirate', + 'plastic', + 'radio', + 'report', + 'signal', + 'sport', + 'studio', + 'subject', + 'super', + 'tango', + 'taxi', + 'tempo', + 'tennis', + 'textile', + 'tokyo', + 'total', + 'tourist', + 'video', + 'visa', + 'academy', + 'alfred', + 'atlanta', + 'atomic', + 'barbara', + 'bazaar', + 'brother', + 'budget', + 'cabaret', + 'cadet', + 'candle', + 'capsule', + 'caviar', + 'channel', + 'chapter', + 'circle', + 'cobalt', + 'comrade', + 'condor', + 'crimson', + 'cyclone', + 'darwin', + 'declare', + 'denver', + 'desert', + 'divide', + 'dolby', + 'domain', + 'double', + 'eagle', + 'echo', + 'eclipse', + 'editor', + 'educate', + 'edward', + 'effect', + 'electra', + 'emerald', + 'emotion', + 'empire', + 'eternal', + 'evening', + 'exhibit', + 'expand', + 'explore', + 'extreme', + 'ferrari', + 'forget', + 'freedom', + 'friday', + 'fuji', + 'galileo', + 'genesis', + 'gravity', + 'habitat', + 'hamlet', + 'harlem', + 'helium', + 'holiday', + 'hunter', + 'ibiza', + 'iceberg', + 'imagine', + 'infant', + 'isotope', + 'jackson', + 'jamaica', + 'jasmine', + 'java', + 'jessica', + 'kitchen', + 'lazarus', + 'letter', + 'license', + 'lithium', + 'loyal', + 'lucky', + 'magenta', + 'manual', + 'marble', + 'maxwell', + 'mayor', + 'monarch', + 'monday', + 'money', + 'morning', + 'mother', + 'mystery', + 'native', + 'nectar', + 'nelson', + 'network', + 'nikita', + 'nobel', + 'nobody', + 'nominal', + 'norway', + 'nothing', + 'number', + 'october', + 'office', + 'oliver', + 'opinion', + 'option', + 'order', + 'outside', + 'package', + 'pandora', + 'panther', + 'papa', + 'pattern', + 'pedro', + 'pencil', + 'people', + 'phantom', + 'philips', + 'pioneer', + 'pluto', + 'podium', + 'portal', + 'potato', + 'process', + 'proxy', + 'pupil', + 'python', + 'quality', + 'quarter', + 'quiet', + 'rabbit', + 'radical', + 'radius', + 'rainbow', + 'ramirez', + 'ravioli', + 'raymond', + 'respect', + 'respond', + 'result', + 'resume', + 'richard', + 'river', + 'roger', + 'roman', + 'rondo', + 'sabrina', + 'salary', + 'salsa', + 'sample', + 'samuel', + 'saturn', + 'savage', + 'scarlet', + 'scorpio', + 'sector', + 'serpent', + 'shampoo', + 'sharon', + 'silence', + 'simple', + 'society', + 'sonar', + 'sonata', + 'soprano', + 'sparta', + 'spider', + 'sponsor', + 'abraham', + 'action', + 'active', + 'actor', + 'adam', + 'address', + 'admiral', + 'adrian', + 'agenda', + 'agent', + 'airline', + 'airport', + 'alabama', + 'aladdin', + 'alarm', + 'algebra', + 'alibi', + 'alice', + 'alien', + 'almond', + 'alpine', + 'amber', + 'amigo', + 'ammonia', + 'analyze', + 'anatomy', + 'angel', + 'annual', + 'answer', + 'apple', + 'archive', + 'arctic', + 'arena', + 'arizona', + 'armada', + 'arnold', + 'arsenal', + 'arthur', + 'asia', + 'aspect', + 'athena', + 'audio', + 'august', + 'austria', + 'avenue', + 'average', + 'axiom', + 'aztec', + 'bagel', + 'baker', + 'balance', + 'ballad', + 'ballet', + 'bambino', + 'bamboo', + 'baron', + 'basic', + 'basket', + 'battery', + 'belgium', + 'benefit', + 'berlin', + 'bermuda', + 'bernard', + 'bicycle', + 'binary', + 'biology', + 'bishop', + 'blitz', + 'block', + 'blonde', + 'bonjour', + 'boris', + 'boston', + 'bottle', + 'boxer', + 'brandy', + 'bravo', + 'brazil', + 'bridge', + 'british', + 'bronze', + 'brown', + 'bruce', + 'bruno', + 'brush', + 'burger', + 'burma', + 'cabinet', + 'cactus', + 'cafe', + 'cairo', + 'calypso', + 'camel', + 'campus', + 'canal', + 'cannon', + 'canoe', + 'cantina', + 'canvas', + 'canyon', + 'capital', + 'caramel', + 'caravan', + 'career', + 'cargo', + 'carlo', + 'carol', + 'carpet', + 'cartel', + 'cartoon', + 'castle', + 'castro', + 'cecilia', + 'cement', + 'center', + 'century', + 'ceramic', + 'chamber', + 'chance', + 'change', + 'chaos', + 'charlie', + 'charm', + 'charter', + 'cheese', + 'chef', + 'chemist', + 'cherry', + 'chess', + 'chicago', + 'chicken', + 'chief', + 'china', + 'cigar', + 'circus', + 'city', + 'clara', + 'classic', + 'claudia', + 'clean', + 'client', + 'climax', + 'clinic', + 'clock', + 'club', + 'cockpit', + 'coconut', + 'cola', + 'collect', + 'colombo', + 'colony', + 'color', + 'combat', + 'comedy', + 'command', + 'company', + 'concert', + 'connect', + 'consul', + 'contact', + 'contour', + 'control', + 'convert', + 'copy', + 'corner', + 'corona', + 'correct', + 'cosmos', + 'couple', + 'courage', + 'cowboy', + 'craft', + 'crash', + 'cricket', + 'crown', + 'cuba', + 'dallas', + 'dance', + 'daniel', + 'decade', + 'decimal', + 'degree', + 'delete', + 'deliver', + 'delphi', + 'deluxe', + 'demand', + 'demo', + 'denmark', + 'derby', + 'design', + 'detect', + 'develop', + 'diagram', + 'diamond', + 'diana', + 'diego', + 'diesel', + 'diet', + 'digital', + 'dilemma', + 'direct', + 'disco', + 'disney', + 'distant', + 'dollar', + 'dolphin', + 'donald', + 'drink', + 'driver', + 'dublin', + 'duet', + 'dynamic', + 'earth', + 'east', + 'ecology', + 'economy', + 'edgar', + 'egypt', + 'elastic', + 'elegant', + 'element', + 'elite', + 'elvis', + 'email', + 'empty', + 'energy', + 'engine', + 'english', + 'episode', + 'equator', + 'escape', + 'escort', + 'ethnic', + 'europe', + 'everest', + 'evident', + 'exact', + 'example', + 'exit', + 'exotic', + 'export', + 'express', + 'factor', + 'falcon', + 'family', + 'fantasy', + 'fashion', + 'fiber', + 'fiction', + 'fidel', + 'fiesta', + 'figure', + 'film', + 'filter', + 'finance', + 'finish', + 'finland', + 'first', + 'flag', + 'flash', + 'florida', + 'flower', + 'fluid', + 'flute', + 'folio', + 'ford', + 'forest', + 'formal', + 'formula', + 'fortune', + 'forward', + 'fragile', + 'france', + 'frank', + 'fresh', + 'friend', + 'frozen', + 'future', + 'gabriel', + 'gamma', + 'garage', + 'garcia', + 'garden', + 'garlic', + 'gemini', + 'general', + 'genetic', + 'genius', + 'germany', + 'gloria', + 'gold', + 'golf', + 'gondola', + 'gong', + 'good', + 'gordon', + 'gorilla', + 'grand', + 'granite', + 'graph', + 'green', + 'group', + 'guide', + 'guitar', + 'guru', + 'hand', + 'happy', + 'harbor', + 'harvard', + 'havana', + 'hawaii', + 'helena', + 'hello', + 'henry', + 'hilton', + 'history', + 'horizon', + 'house', + 'human', + 'icon', + 'idea', + 'igloo', + 'igor', + 'image', + 'impact', + 'import', + 'india', + 'indigo', + 'input', + 'insect', + 'instant', + 'iris', + 'italian', + 'jacket', + 'jacob', + 'jaguar', + 'janet', + 'jargon', + 'jazz', + 'jeep', + 'john', + 'joker', + 'jordan', + 'judo', + 'jumbo', + 'june', + 'jungle', + 'junior', + 'jupiter', + 'karate', + 'karma', + 'kayak', + 'kermit', + 'king', + 'koala', + 'korea', + 'labor', + 'lady', + 'lagoon', + 'laptop', + 'laser', + 'latin', + 'lava', + 'lecture', + 'left', + 'legal', + 'level', + 'lexicon', + 'liberal', + 'libra', + 'lily', + 'limbo', + 'limit', + 'linda', + 'linear', + 'lion', + 'liquid', + 'little', + 'llama', + 'lobby', + 'lobster', + 'local', + 'logic', + 'logo', + 'lola', + 'london', + 'lucas', + 'lunar', + 'machine', + 'macro', + 'madam', + 'madonna', + 'madrid', + 'maestro', + 'magic', + 'magnet', + 'magnum', + 'mailbox', + 'major', + 'mama', + 'mambo', + 'manager', + 'manila', + 'marco', + 'marina', + 'market', + 'mars', + 'martin', + 'marvin', + 'mary', + 'master', + 'matrix', + 'maximum', + 'media', + 'medical', + 'mega', + 'melody', + 'memo', + 'mental', + 'mentor', + 'mercury', + 'message', + 'metal', + 'meteor', + 'method', + 'mexico', + 'miami', + 'micro', + 'milk', + 'million', + 'minimum', + 'minus', + 'minute', + 'miracle', + 'mirage', + 'miranda', + 'mister', + 'mixer', + 'mobile', + 'modem', + 'modern', + 'modular', + 'moment', + 'monaco', + 'monica', + 'monitor', + 'mono', + 'monster', + 'montana', + 'morgan', + 'motel', + 'motif', + 'motor', + 'mozart', + 'multi', + 'museum', + 'mustang', + 'natural', + 'neon', + 'nepal', + 'neptune', + 'nerve', + 'neutral', + 'nevada', + 'news', + 'next', + 'ninja', + 'nirvana', + 'normal', + 'nova', + 'novel', + 'nuclear', + 'numeric', + 'nylon', + 'oasis', + 'observe', + 'ocean', + 'octopus', + 'olivia', + 'olympic', + 'omega', + 'opera', + 'optic', + 'optimal', + 'orange', + 'orbit', + 'organic', + 'orient', + 'origin', + 'orlando', + 'oscar', + 'oxford', + 'oxygen', + 'ozone', + 'pablo', + 'pacific', + 'pagoda', + 'palace', + 'pamela', + 'panama', + 'pancake', + 'panda', + 'panel', + 'panic', + 'paradox', + 'pardon', + 'paris', + 'parker', + 'parking', + 'parody', + 'partner', + 'passage', + 'passive', + 'pasta', + 'pastel', + 'patent', + 'patient', + 'patriot', + 'patrol', + 'pegasus', + 'pelican', + 'penguin', + 'pepper', + 'percent', + 'perfect', + 'perfume', + 'period', + 'permit', + 'person', + 'peru', + 'phone', + 'photo', + 'picasso', + 'picnic', + 'picture', + 'pigment', + 'pilgrim', + 'pilot', + 'pixel', + 'pizza', + 'planet', + 'plasma', + 'plaza', + 'pocket', + 'poem', + 'poetic', + 'poker', + 'polaris', + 'police', + 'politic', + 'polo', + 'polygon', + 'pony', + 'popcorn', + 'popular', + 'postage', + 'precise', + 'prefix', + 'premium', + 'present', + 'price', + 'prince', + 'printer', + 'prism', + 'private', + 'prize', + 'product', + 'profile', + 'program', + 'project', + 'protect', + 'proton', + 'public', + 'pulse', + 'puma', + 'pump', + 'pyramid', + 'queen', + 'radar', + 'ralph', + 'random', + 'rapid', + 'rebel', + 'record', + 'recycle', + 'reflex', + 'reform', + 'regard', + 'regular', + 'relax', + 'reptile', + 'reverse', + 'ricardo', + 'right', + 'ringo', + 'risk', + 'ritual', + 'robert', + 'robot', + 'rocket', + 'rodeo', + 'romeo', + 'royal', + 'russian', + 'safari', + 'salad', + 'salami', + 'salmon', + 'salon', + 'salute', + 'samba', + 'sandra', + 'santana', + 'sardine', + 'school', + 'scoop', + 'scratch', + 'screen', + 'script', + 'scroll', + 'second', + 'secret', + 'section', + 'segment', + 'select', + 'seminar', + 'senator', + 'senior', + 'sensor', + 'serial', + 'service', + 'shadow', + 'sharp', + 'sheriff', + 'shock', + 'short', + 'shrink', + 'sierra', + 'silicon', + 'silk', + 'silver', + 'similar', + 'simon', + 'single', + 'siren', + 'slang', + 'slogan', + 'smart', + 'smoke', + 'snake', + 'social', + 'soda', + 'solar', + 'solid', + 'solo', + 'sonic', + 'source', + 'soviet', + 'special', + 'speed', + 'sphere', + 'spiral', + 'spirit', + 'spring', + 'static', + 'status', + 'stereo', + 'stone', + 'stop', + 'street', + 'strong', + 'student', + 'style', + 'sultan', + 'susan', + 'sushi', + 'suzuki', + 'switch', + 'symbol', + 'system', + 'tactic', + 'tahiti', + 'talent', + 'tarzan', + 'telex', + 'texas', + 'theory', + 'thermos', + 'tiger', + 'titanic', + 'tomato', + 'topic', + 'tornado', + 'toronto', + 'torpedo', + 'totem', + 'tractor', + 'traffic', + 'transit', + 'trapeze', + 'travel', + 'tribal', + 'trick', + 'trident', + 'trilogy', + 'tripod', + 'tropic', + 'trumpet', + 'tulip', + 'tuna', + 'turbo', + 'twist', + 'ultra', + 'uniform', + 'union', + 'uranium', + 'vacuum', + 'valid', + 'vampire', + 'vanilla', + 'vatican', + 'velvet', + 'ventura', + 'venus', + 'vertigo', + 'veteran', + 'victor', + 'vienna', + 'viking', + 'village', + 'vincent', + 'violet', + 'violin', + 'virtual', + 'virus', + 'vision', + 'visitor', + 'visual', + 'vitamin', + 'viva', + 'vocal', + 'vodka', + 'volcano', + 'voltage', + 'volume', + 'voyage', + 'water', + 'weekend', + 'welcome', + 'western', + 'window', + 'winter', + 'wizard', + 'wolf', + 'world', + 'xray', + 'yankee', + 'yoga', + 'yogurt', + 'yoyo', + 'zebra', + 'zero', + 'zigzag', + 'zipper', + 'zodiac', + 'zoom', + 'acid', + 'adios', + 'agatha', + 'alamo', + 'alert', + 'almanac', + 'aloha', + 'andrea', + 'anita', + 'arcade', + 'aurora', + 'avalon', + 'baby', + 'baggage', + 'balloon', + 'bank', + 'basil', + 'begin', + 'biscuit', + 'blue', + 'bombay', + 'botanic', + 'brain', + 'brenda', + 'brigade', + 'cable', + 'calibre', + 'carmen', + 'cello', + 'celtic', + 'chariot', + 'chrome', + 'citrus', + 'civil', + 'cloud', + 'combine', + 'common', + 'cool', + 'copper', + 'coral', + 'crater', + 'cubic', + 'cupid', + 'cycle', + 'depend', + 'door', + 'dream', + 'dynasty', + 'edison', + 'edition', + 'enigma', + 'equal', + 'eric', + 'event', + 'evita', + 'exodus', + 'extend', + 'famous', + 'farmer', + 'food', + 'fossil', + 'frog', + 'fruit', + 'geneva', + 'gentle', + 'george', + 'giant', + 'gilbert', + 'gossip', + 'gram', + 'greek', + 'grille', + 'hammer', + 'harvest', + 'hazard', + 'heaven', + 'herbert', + 'heroic', + 'hexagon', + 'husband', + 'immune', + 'inca', + 'inch', + 'initial', + 'isabel', + 'ivory', + 'jason', + 'jerome', + 'joel', + 'joshua', + 'journal', + 'judge', + 'juliet', + 'jump', + 'justice', + 'kimono', + 'kinetic', + 'leonid', + 'leopard', + 'lima', + 'maze', + 'medusa', + 'member', + 'memphis', + 'michael', + 'miguel', + 'milan', + 'mile', + 'miller', + 'mimic', + 'mimosa', + 'mission', + 'monkey', + 'moral', + 'moses', + 'mouse', + 'nancy', + 'natasha', + 'nebula', + 'nickel', + 'nina', + 'noise', + 'orchid', + 'oregano', + 'origami', + 'orinoco', + 'orion', + 'othello', + 'paper', + 'paprika', + 'prelude', + 'prepare', + 'pretend', + 'promise', + 'prosper', + 'provide', + 'puzzle', + 'remote', + 'repair', + 'reply', + 'rival', + 'riviera', + 'robin', + 'rose', + 'rover', + 'rudolf', + 'saga', + 'sahara', + 'scholar', + 'shelter', + 'ship', + 'shoe', + 'sigma', + 'sister', + 'sleep', + 'smile', + 'spain', + 'spark', + 'split', + 'spray', + 'square', + 'stadium', + 'star', + 'storm', + 'story', + 'strange', + 'stretch', + 'stuart', + 'subway', + 'sugar', + 'sulfur', + 'summer', + 'survive', + 'sweet', + 'swim', + 'table', + 'taboo', + 'target', + 'teacher', + 'telecom', + 'temple', + 'tibet', + 'ticket', + 'tina', + 'today', + 'toga', + 'tommy', + 'tower', + 'trivial', + 'tunnel', + 'turtle', + 'twin', + 'uncle', + 'unicorn', + 'unique', + 'update', + 'valery', + 'vega', + 'version', + 'voodoo', + 'warning', + 'william', + 'wonder', + 'year', + 'yellow', + 'young', + 'absent', + 'absorb', + 'absurd', + 'accent', + 'alfonso', + 'alias', + 'ambient', + 'anagram', + 'andy', + 'anvil', + 'appear', + 'apropos', + 'archer', + 'ariel', + 'armor', + 'arrow', + 'austin', + 'avatar', + 'axis', + 'baboon', + 'bahama', + 'bali', + 'balsa', + 'barcode', + 'bazooka', + 'beach', + 'beast', + 'beatles', + 'beauty', + 'before', + 'benny', + 'betty', + 'between', + 'beyond', + 'billy', + 'bison', + 'blast', + 'bless', + 'bogart', + 'bonanza', + 'book', + 'border', + 'brave', + 'bread', + 'break', + 'broken', + 'bucket', + 'buenos', + 'buffalo', + 'bundle', + 'button', + 'buzzer', + 'byte', + 'caesar', + 'camilla', + 'canary', + 'candid', + 'carrot', + 'cave', + 'chant', + 'child', + 'choice', + 'chris', + 'cipher', + 'clarion', + 'clark', + 'clever', + 'cliff', + 'clone', + 'conan', + 'conduct', + 'congo', + 'costume', + 'cotton', + 'cover', + 'crack', + 'current', + 'danube', + 'data', + 'decide', + 'deposit', + 'desire', + 'detail', + 'dexter', + 'dinner', + 'donor', + 'druid', + 'drum', + 'easy', + 'eddie', + 'enjoy', + 'enrico', + 'epoxy', + 'erosion', + 'except', + 'exile', + 'explain', + 'fame', + 'fast', + 'father', + 'felix', + 'field', + 'fiona', + 'fire', + 'fish', + 'flame', + 'flex', + 'flipper', + 'float', + 'flood', + 'floor', + 'forbid', + 'forever', + 'fractal', + 'frame', + 'freddie', + 'front', + 'fuel', + 'gallop', + 'game', + 'garbo', + 'gate', + 'gelatin', + 'gibson', + 'ginger', + 'giraffe', + 'gizmo', + 'glass', + 'goblin', + 'gopher', + 'grace', + 'gray', + 'gregory', + 'grid', + 'griffin', + 'ground', + 'guest', + 'gustav', + 'gyro', + 'hair', + 'halt', + 'harris', + 'heart', + 'heavy', + 'herman', + 'hippie', + 'hobby', + 'honey', + 'hope', + 'horse', + 'hostel', + 'hydro', + 'imitate', + 'info', + 'ingrid', + 'inside', + 'invent', + 'invest', + 'invite', + 'ivan', + 'james', + 'jester', + 'jimmy', + 'join', + 'joseph', + 'juice', + 'julius', + 'july', + 'kansas', + 'karl', + 'kevin', + 'kiwi', + 'ladder', + 'lake', + 'laura', + 'learn', + 'legacy', + 'legend', + 'lesson', + 'life', + 'light', + 'list', + 'locate', + 'lopez', + 'lorenzo', + 'love', + 'lunch', + 'malta', + 'mammal', + 'margin', + 'margo', + 'marion', + 'mask', + 'match', + 'mayday', + 'meaning', + 'mercy', + 'middle', + 'mike', + 'mirror', + 'modest', + 'morph', + 'morris', + 'mystic', + 'nadia', + 'nato', + 'navy', + 'needle', + 'neuron', + 'never', + 'newton', + 'nice', + 'night', + 'nissan', + 'nitro', + 'nixon', + 'north', + 'oberon', + 'octavia', + 'ohio', + 'olga', + 'open', + 'opus', + 'orca', + 'oval', + 'owner', + 'page', + 'paint', + 'palma', + 'parent', + 'parlor', + 'parole', + 'paul', + 'peace', + 'pearl', + 'perform', + 'phoenix', + 'phrase', + 'pierre', + 'pinball', + 'place', + 'plate', + 'plato', + 'plume', + 'pogo', + 'point', + 'polka', + 'poncho', + 'powder', + 'prague', + 'press', + 'presto', + 'pretty', + 'prime', + 'promo', + 'quest', + 'quick', + 'quiz', + 'quota', + 'race', + 'rachel', + 'raja', + 'ranger', + 'region', + 'remark', + 'rent', + 'reward', + 'rhino', + 'ribbon', + 'rider', + 'road', + 'rodent', + 'round', + 'rubber', + 'ruby', + 'rufus', + 'sabine', + 'saddle', + 'sailor', + 'saint', + 'salt', + 'scale', + 'scuba', + 'season', + 'secure', + 'shake', + 'shallow', + 'shannon', + 'shave', + 'shelf', + 'sherman', + 'shine', + 'shirt', + 'side', + 'sinatra', + 'sincere', + 'size', + 'slalom', + 'slow', + 'small', + 'snow', + 'sofia', + 'song', + 'sound', + 'south', + 'speech', + 'spell', + 'spend', + 'spoon', + 'stage', + 'stamp', + 'stand', + 'state', + 'stella', + 'stick', + 'sting', + 'stock', + 'store', + 'sunday', + 'sunset', + 'support', + 'supreme', + 'sweden', + 'swing', + 'tape', + 'tavern', + 'think', + 'thomas', + 'tictac', + 'time', + 'toast', + 'tobacco', + 'tonight', + 'torch', + 'torso', + 'touch', + 'toyota', + 'trade', + 'tribune', + 'trinity', + 'triton', + 'truck', + 'trust', + 'type', + 'under', + 'unit', + 'urban', + 'urgent', + 'user', + 'value', + 'vendor', + 'venice', + 'verona', + 'vibrate', + 'virgo', + 'visible', + 'vista', + 'vital', + 'voice', + 'vortex', + 'waiter', + 'watch', + 'wave', + 'weather', + 'wedding', + 'wheel', + 'whiskey', + 'wisdom', + 'android', + 'annex', + 'armani', + 'cake', + 'confide', + 'deal', + 'define', + 'dispute', + 'genuine', + 'idiom', + 'impress', + 'include', + 'ironic', + 'null', + 'nurse', + 'obscure', + 'prefer', + 'prodigy', + 'ego', + 'fax', + 'jet', + 'job', + 'rio', + 'ski', + 'yes', +]; diff --git a/lib/random.ts b/lib/random.ts index 807570753de3..497f742f0341 100644 --- a/lib/random.ts +++ b/lib/random.ts @@ -7,3 +7,10 @@ import { Random } from 'meteor/random'; * @returns a unique identifier, such as `"Jjwjg6gouWLXhMGKW"`, that is likely to be unique in the whole world */ export const getRandomId = (length?: number): string => Random.id(length); + +/** + * Facade for Meteor's `Random.fraction` function + * + * @returns a strong random number between 0 and 1 + */ +export const getRandomFraction = (): number => Random.fraction(); From 01934bcbf3f2e56364f792a32e140c52ac5e711d Mon Sep 17 00:00:00 2001 From: eduardofcabrera <64327259+eduardofcabrera@users.noreply.github.com> Date: Mon, 21 Feb 2022 14:43:59 -0300 Subject: [PATCH 25/37] Chore: Js to ts slash commands archive (#24304) * convert slashCommands archive files to ts * Syntax fix * Lint fix --- .../client/client.js | 7 -- .../client/client.ts | 15 ++++ .../client/{index.js => index.ts} | 0 .../server/{index.js => index.ts} | 0 .../server/server.js | 79 ------------------ .../server/server.ts | 80 +++++++++++++++++++ 6 files changed, 95 insertions(+), 86 deletions(-) delete mode 100644 app/slashcommands-archiveroom/client/client.js create mode 100644 app/slashcommands-archiveroom/client/client.ts rename app/slashcommands-archiveroom/client/{index.js => index.ts} (100%) rename app/slashcommands-archiveroom/server/{index.js => index.ts} (100%) delete mode 100644 app/slashcommands-archiveroom/server/server.js create mode 100644 app/slashcommands-archiveroom/server/server.ts diff --git a/app/slashcommands-archiveroom/client/client.js b/app/slashcommands-archiveroom/client/client.js deleted file mode 100644 index 31cd75a169f8..000000000000 --- a/app/slashcommands-archiveroom/client/client.js +++ /dev/null @@ -1,7 +0,0 @@ -import { slashCommands } from '../../utils'; - -slashCommands.add('archive', null, { - description: 'Archive', - params: '#channel', - permission: 'archive-room', -}); diff --git a/app/slashcommands-archiveroom/client/client.ts b/app/slashcommands-archiveroom/client/client.ts new file mode 100644 index 000000000000..bee69247b2f2 --- /dev/null +++ b/app/slashcommands-archiveroom/client/client.ts @@ -0,0 +1,15 @@ +import { slashCommands } from '../../utils/lib/slashCommand'; + +slashCommands.add( + 'archive', + undefined, + { + description: 'Archive', + params: '#channel', + permission: 'archive-room', + }, + undefined, + false, + undefined, + undefined, +); diff --git a/app/slashcommands-archiveroom/client/index.js b/app/slashcommands-archiveroom/client/index.ts similarity index 100% rename from app/slashcommands-archiveroom/client/index.js rename to app/slashcommands-archiveroom/client/index.ts diff --git a/app/slashcommands-archiveroom/server/index.js b/app/slashcommands-archiveroom/server/index.ts similarity index 100% rename from app/slashcommands-archiveroom/server/index.js rename to app/slashcommands-archiveroom/server/index.ts diff --git a/app/slashcommands-archiveroom/server/server.js b/app/slashcommands-archiveroom/server/server.js deleted file mode 100644 index 66c164bbf1c8..000000000000 --- a/app/slashcommands-archiveroom/server/server.js +++ /dev/null @@ -1,79 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; -import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; - -import { Rooms, Messages } from '../../models'; -import { slashCommands } from '../../utils'; -import { api } from '../../../server/sdk/api'; - -function Archive(command, params, item) { - if (command !== 'archive' || !Match.test(params, String)) { - return; - } - - let channel = params.trim(); - let room; - - if (channel === '') { - room = Rooms.findOneById(item.rid); - channel = room.name; - } else { - channel = channel.replace('#', ''); - room = Rooms.findOneByName(channel); - } - - const user = Meteor.users.findOne(Meteor.userId()); - - if (!room) { - api.broadcast('notify.ephemeralMessage', Meteor.userId(), item.rid, { - msg: TAPi18n.__( - 'Channel_doesnt_exist', - { - postProcess: 'sprintf', - sprintf: [channel], - }, - user.language, - ), - }); - } - - // You can not archive direct messages. - if (room.t === 'd') { - return; - } - - if (room.archived) { - api.broadcast('notify.ephemeralMessage', Meteor.userId(), item.rid, { - msg: TAPi18n.__( - 'Duplicate_archived_channel_name', - { - postProcess: 'sprintf', - sprintf: [channel], - }, - user.language, - ), - }); - return; - } - Meteor.call('archiveRoom', room._id); - - Messages.createRoomArchivedByRoomIdAndUser(room._id, Meteor.user()); - api.broadcast('notify.ephemeralMessage', Meteor.userId(), item.rid, { - msg: TAPi18n.__( - 'Channel_Archived', - { - postProcess: 'sprintf', - sprintf: [channel], - }, - user.language, - ), - }); - - return Archive; -} - -slashCommands.add('archive', Archive, { - description: 'Archive', - params: '#channel', - permission: 'archive-room', -}); diff --git a/app/slashcommands-archiveroom/server/server.ts b/app/slashcommands-archiveroom/server/server.ts new file mode 100644 index 000000000000..d7349278c10c --- /dev/null +++ b/app/slashcommands-archiveroom/server/server.ts @@ -0,0 +1,80 @@ +import { Meteor } from 'meteor/meteor'; +import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; + +import { Rooms, Messages } from '../../models/server'; +import { slashCommands } from '../../utils/lib/slashCommand'; +import { api } from '../../../server/sdk/api'; +import { settings } from '../../settings/server'; + +function Archive(_command: 'archive', params: string, item: Record): void | Function { + let channel = params.trim(); + + let room; + + if (channel === '') { + room = Rooms.findOneById(item.rid); + channel = room.name; + } else { + channel = channel.replace('#', ''); + room = Rooms.findOneByName(channel); + } + + const userId = Meteor.userId(); + + if (!userId) { + return; + } + + if (!room) { + api.broadcast('notify.ephemeralMessage', userId, item.rid, { + msg: TAPi18n.__('Channel_doesnt_exist', { + postProcess: 'sprintf', + sprintf: [channel], + lng: settings.get('Language') || 'en', + }), + }); + return; + } + + // You can not archive direct messages. + if (room.t === 'd') { + return; + } + + if (room.archived) { + api.broadcast('notify.ephemeralMessage', userId, item.rid, { + msg: TAPi18n.__('Duplicate_archived_channel_name', { + postProcess: 'sprintf', + sprintf: [channel], + lng: settings.get('Language') || 'en', + }), + }); + return; + } + Meteor.call('archiveRoom', room._id); + + Messages.createRoomArchivedByRoomIdAndUser(room._id, Meteor.user()); + api.broadcast('notify.ephemeralMessage', userId, item.rid, { + msg: TAPi18n.__('Channel_Archived', { + postProcess: 'sprintf', + sprintf: [channel], + lng: settings.get('Language') || 'en', + }), + }); + + return Archive; +} + +slashCommands.add( + 'archive', + Archive, + { + description: 'Archive', + params: '#channel', + permission: 'archive-room', + }, + undefined, + false, + undefined, + undefined, +); From 737ed1e37862d28f1cbad9ab6cbfad0d819e5d1a Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Mon, 21 Feb 2022 23:44:59 +0530 Subject: [PATCH 26/37] [IMPROVE] Skip encryption for slash commands in E2E rooms (#24475) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * skip e2e encryption for slash commands * requested changes Co-authored-by: Fábio Albuquerque --- app/e2e/client/rocketchat.e2e.room.js | 6 +++++- client/startup/e2e.ts | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/e2e/client/rocketchat.e2e.room.js b/app/e2e/client/rocketchat.e2e.room.js index 0f2344dbe144..1c74ef99ad41 100644 --- a/app/e2e/client/rocketchat.e2e.room.js +++ b/app/e2e/client/rocketchat.e2e.room.js @@ -145,7 +145,7 @@ export class E2ERoom extends Emitter { this.setState(E2ERoomState.KEYS_RECEIVED); } - async shouldConvertSentMessages() { + async shouldConvertSentMessages(message) { if (!this.isReady() || this[PAUSED]) { return false; } @@ -156,6 +156,10 @@ export class E2ERoom extends Emitter { }); } + if (message.msg[0] === '/') { + return false; + } + return true; } diff --git a/client/startup/e2e.ts b/client/startup/e2e.ts index 99ff7ce0c5a5..b80c1f3955dc 100644 --- a/client/startup/e2e.ts +++ b/client/startup/e2e.ts @@ -116,7 +116,9 @@ Meteor.startup(() => { subscription.encrypted ? e2eRoom.resume() : e2eRoom.pause(); - if (!(await e2eRoom.shouldConvertSentMessages())) { + const shouldConvertSentMessages = await e2eRoom.shouldConvertSentMessages(message); + + if (!shouldConvertSentMessages) { return message; } From c2202cb38e0dc0b96981187776791e4e0e42d69d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrique=20Guimar=C3=A3es=20Ribeiro?= Date: Mon, 21 Feb 2022 15:26:06 -0300 Subject: [PATCH 27/37] [NEW] [IMPROVE] Purchase Type Filter for marketplace apps and Categories filter anchor refactoring (#24454) * refactor: Change category filter anchor design Changed the design of the category filter anchor from a button with an icon to a Select with a dynamic text inside * WIP * Finished Free Paid filter visuals * feat: :sparkles: Implement possible app purchase type filtering logic Implemented the logic necessary for the purchase type filter to work, inserted a necessary translation in the dictionary and made some changes to the app TS type so that it works as it should. The App TS type had purchaseType typed as 'unknown[]' and price as 'string' * Translation hotfix * refactor: :art: Possible refactor of the categories filter anchor I've refactored the categories filter Anchor from a select dropdown with boolean placeholders to a Button dropdown with improved looks and a selected categories counter. * refactor: :art: Refactor category dropdown anchor to fit better with design specs I've refactored the way the category filter anchor reacts to category selections. Now when no categories are selected the filter looks like a common fuselage select, but when there are selected categories it looks like a blue button with a white counter on it. Also solved some type errors. * lock * Lock * lock * Reviews * Removing unnecessary comment Co-authored-by: dougfabris --- client/components/FilterByText.tsx | 6 ++- client/views/admin/apps/AppsTable.tsx | 21 ++++++-- .../components/CategoryDropDown.stories.tsx | 12 +++-- .../apps/components/CategoryDropDown.tsx | 40 ++++++++------ .../components/CategoryDropDownAnchor.tsx | 53 ++++++++++++++++--- ...istWrapper.tsx => DropDownListWrapper.tsx} | 4 +- .../components/FreePaidDropDown.stories.tsx | 43 +++++++++++++++ .../apps/components/FreePaidDropDown.tsx | 42 +++++++++++++++ .../components/FreePaidDropDownAnchor.tsx | 16 ++++++ .../apps/components/FreePaidDropDownList.tsx | 21 ++++++++ .../FreePaidDropDownDefinitions.ts | 26 +++++++++ .../apps/helpers/filterAppByPurchaseType.ts | 8 +++ .../admin/apps/helpers/isValidReference.tsx | 6 +++ .../admin/apps/helpers/preventSideEffects.ts | 5 ++ .../views/admin/apps/hooks/useFilteredApps.ts | 15 +++++- .../admin/apps/hooks/useFreePaidToggle.ts | 18 +++++++ client/views/admin/apps/types.ts | 4 +- package-lock.json | 2 +- packages/rocketchat-i18n/i18n/en.i18n.json | 7 ++- 19 files changed, 308 insertions(+), 41 deletions(-) rename client/views/admin/apps/components/{CategoryDropDownListWrapper.tsx => DropDownListWrapper.tsx} (81%) create mode 100644 client/views/admin/apps/components/FreePaidDropDown.stories.tsx create mode 100644 client/views/admin/apps/components/FreePaidDropDown.tsx create mode 100644 client/views/admin/apps/components/FreePaidDropDownAnchor.tsx create mode 100644 client/views/admin/apps/components/FreePaidDropDownList.tsx create mode 100644 client/views/admin/apps/definitions/FreePaidDropDownDefinitions.ts create mode 100644 client/views/admin/apps/helpers/filterAppByPurchaseType.ts create mode 100644 client/views/admin/apps/helpers/isValidReference.tsx create mode 100644 client/views/admin/apps/helpers/preventSideEffects.ts create mode 100644 client/views/admin/apps/hooks/useFreePaidToggle.ts diff --git a/client/components/FilterByText.tsx b/client/components/FilterByText.tsx index 82689a1ba4c6..04abdd00a0a8 100644 --- a/client/components/FilterByText.tsx +++ b/client/components/FilterByText.tsx @@ -49,7 +49,11 @@ const FilterByText: FC = ({ placeholder, onChange: setFilter, {props.textButton} ) : ( - children && {children} + children && ( + + {children} + + ) )}
); diff --git a/client/views/admin/apps/AppsTable.tsx b/client/views/admin/apps/AppsTable.tsx index 1b3cbc9c1d1e..ab407bf7e9cb 100644 --- a/client/views/admin/apps/AppsTable.tsx +++ b/client/views/admin/apps/AppsTable.tsx @@ -14,7 +14,7 @@ import { Icon, } from '@rocket.chat/fuselage'; import { useDebouncedState } from '@rocket.chat/fuselage-hooks'; -import React, { FC, useMemo } from 'react'; +import React, { FC, useMemo, useState } from 'react'; import FilterByText from '../../../components/FilterByText'; import { @@ -34,9 +34,11 @@ import AppRow from './AppRow'; import { useAppsReload, useAppsResult } from './AppsContext'; import MarketplaceRow from './MarketplaceRow'; import CategoryDropDown from './components/CategoryDropDown'; +import FreePaidDropDown from './components/FreePaidDropDown'; import TagList from './components/TagList'; import { useCategories } from './hooks/useCategories'; import { useFilteredApps } from './hooks/useFilteredApps'; +import { useFreePaidToggle } from './hooks/useFreePaidToggle'; const AppsTable: FC<{ isMarketplace: boolean; @@ -65,6 +67,17 @@ const AppsTable: FC<{ const [categories, selectedCategories, categoryTagList, onSelected] = useCategories(); + const [freePaidFilterStructure, setFreePaidFilterStructure] = useState({ + label: t('Filter_By_Price'), + items: [ + { id: 'all', label: t('All_Apps'), checked: true }, + { id: 'free', label: t('Free_Apps'), checked: false }, + { id: 'paid', label: t('Paid_Apps'), checked: false }, + ], + }); + + const freePaidFilterOnSelected = useFreePaidToggle(setFreePaidFilterStructure); + const appsResult = useFilteredApps({ appsData: isMarketplace ? marketplaceApps : installedApps, text, @@ -72,12 +85,15 @@ const AppsTable: FC<{ itemsPerPage, sortDirection, categories: useMemo(() => selectedCategories.map(({ label }) => label), [selectedCategories]), + purchaseType: useMemo(() => freePaidFilterStructure.items.find(({ checked }) => checked)?.id, [freePaidFilterStructure]), }); return ( <> + {/* TODO Divide into two components: Filters and AppsTable */} setText(text)}> - + + {(appsResult.phase === AsyncStatePhase.LOADING || @@ -181,7 +197,6 @@ const AppsTable: FC<{ )} - {/* TODO: Create error variations for empty search message */} {appsResult.phase === AsyncStatePhase.REJECTED && ( diff --git a/client/views/admin/apps/components/CategoryDropDown.stories.tsx b/client/views/admin/apps/components/CategoryDropDown.stories.tsx index e44bcb262b09..afac8deb36d7 100644 --- a/client/views/admin/apps/components/CategoryDropDown.stories.tsx +++ b/client/views/admin/apps/components/CategoryDropDown.stories.tsx @@ -55,7 +55,7 @@ const testGroup: CategoryDropDownListProps['groups'] = [ }, ]; -export const Achor: Story = () => ; +export const Anchor: Story = () => group.items).length} />; export const List: Story = () => { const [data, setData] = useState(() => testGroup); @@ -65,14 +65,16 @@ export const List: Story = () => { }; export const Default: Story = () => { - const [data, , categoryTagList, onSelected] = useCategories(); + const [, selectedCategories, categoryTagList] = useCategories(); + + const [data, setData] = useState(() => testGroup); + + const onSelected = useCategoryToggle(setData); return ( <> - - - + diff --git a/client/views/admin/apps/components/CategoryDropDown.tsx b/client/views/admin/apps/components/CategoryDropDown.tsx index 2efb4ca790ef..6a584422da22 100644 --- a/client/views/admin/apps/components/CategoryDropDown.tsx +++ b/client/views/admin/apps/components/CategoryDropDown.tsx @@ -1,29 +1,30 @@ import { useToggle } from '@rocket.chat/fuselage-hooks'; -import React, { useRef, FC, useCallback, ComponentProps } from 'react'; +import React, { useRef, FC, useCallback } from 'react'; -import { CategoryDropDownListProps } from '../definitions/CategoryDropdownDefinitions'; +import { CategoryDropdownItem, CategoryDropDownListProps } from '../definitions/CategoryDropdownDefinitions'; +import { isValidReference } from '../helpers/isValidReference'; +import { onMouseEventPreventSideEffects } from '../helpers/preventSideEffects'; import CategoryDropDownAnchor from './CategoryDropDownAnchor'; import CategoryDropDownList from './CategoryDropDownList'; -import CategoryDropDownListWrapper from './CategoryDropDownListWrapper'; +import DropDownListWrapper from './DropDownListWrapper'; -const CategoryDropDown: FC< - { - data: CategoryDropDownListProps['groups']; - onSelected: CategoryDropDownListProps['onSelected']; - } & Partial, 'small' | 'mini'>> -> = ({ data, onSelected, ...props }) => { - const reference = useRef(null); +const CategoryDropDown: FC<{ + data: CategoryDropDownListProps['groups']; + onSelected: CategoryDropDownListProps['onSelected']; + selectedCategories: (CategoryDropdownItem & { checked: true })[]; +}> = ({ data, onSelected, selectedCategories, ...props }) => { + const reference = useRef(null); const [collapsed, toggleCollapsed] = useToggle(false); const onClose = useCallback( (e) => { - if (e.target !== reference.current && !reference.current?.contains(e.target)) { + if (isValidReference(reference, e)) { toggleCollapsed(false); return; } - e.preventDefault(); - e.stopPropagation(); - e.stopImmediatePropagation(); + + onMouseEventPreventSideEffects(e); + return false; }, [toggleCollapsed], @@ -31,11 +32,16 @@ const CategoryDropDown: FC< return ( <> - + {collapsed && ( - + - + )} ); diff --git a/client/views/admin/apps/components/CategoryDropDownAnchor.tsx b/client/views/admin/apps/components/CategoryDropDownAnchor.tsx index 61a0705ecb5a..49289fc5e5d3 100644 --- a/client/views/admin/apps/components/CategoryDropDownAnchor.tsx +++ b/client/views/admin/apps/components/CategoryDropDownAnchor.tsx @@ -1,11 +1,50 @@ -import { ActionButton } from '@rocket.chat/fuselage'; +import { Box, Button, Icon, Select } from '@rocket.chat/fuselage'; import React, { ComponentProps, forwardRef } from 'react'; -const CategoryDropDownAnchor = forwardRef>>(function CategoryDropDownAnchor( - props, - ref, -) { - return ; -}); +import { useTranslation } from '../../../../contexts/TranslationContext'; + +const CategoryDropDownAnchor = forwardRef> & { selectedCategoriesCount: number }>( + function CategoryDropDownAnchor(props, ref) { + const t = useTranslation(); + + return ( + + ); + }, +); export default CategoryDropDownAnchor; diff --git a/client/views/admin/apps/components/CategoryDropDownListWrapper.tsx b/client/views/admin/apps/components/DropDownListWrapper.tsx similarity index 81% rename from client/views/admin/apps/components/CategoryDropDownListWrapper.tsx rename to client/views/admin/apps/components/DropDownListWrapper.tsx index 052a92f4ab72..ae2b1ca2b8e9 100644 --- a/client/views/admin/apps/components/CategoryDropDownListWrapper.tsx +++ b/client/views/admin/apps/components/DropDownListWrapper.tsx @@ -13,7 +13,7 @@ const hidden = { position: 'fixed', } as const; -const CategoryDropDownListWrapper = forwardRef & { onClose: (e: unknown) => void }>( +const DropDownListWrapper = forwardRef & { onClose: (e: MouseEvent) => void }>( function CategoryDropDownListWrapper({ children, onClose }, ref) { const target = useRef(null); useOutsideClick([target], onClose); @@ -26,4 +26,4 @@ const CategoryDropDownListWrapper = forwardRef ; + +export const List: Story = () => { + const [data, setData] = useState(() => testGroup); + + const onSelected = useFreePaidToggle(setData); + + return ; +}; + +export const Default: Story = () => { + const [data, setData] = useState(() => testGroup); + + const onSelected = useFreePaidToggle(setData); + + return ( + <> + + + ); +}; diff --git a/client/views/admin/apps/components/FreePaidDropDown.tsx b/client/views/admin/apps/components/FreePaidDropDown.tsx new file mode 100644 index 000000000000..a517137d64ab --- /dev/null +++ b/client/views/admin/apps/components/FreePaidDropDown.tsx @@ -0,0 +1,42 @@ +import { useToggle } from '@rocket.chat/fuselage-hooks'; +import React, { FC, useCallback, useRef } from 'react'; + +import { FreePaidDropDownProps } from '../definitions/FreePaidDropDownDefinitions'; +import { isValidReference } from '../helpers/isValidReference'; +import { onMouseEventPreventSideEffects } from '../helpers/preventSideEffects'; +import DropDownListWrapper from './DropDownListWrapper'; +import FreePaidDropDownAnchor from './FreePaidDropDownAnchor'; +import FreePaidDropDownList from './FreePaidDropDownList'; + +const FreePaidDropDown: FC = ({ group, onSelected, ...props }) => { + const reference = useRef(null); + const [collapsed, toggleCollapsed] = useToggle(false); + + const onClose = useCallback( + (e) => { + if (isValidReference(reference, e)) { + toggleCollapsed(false); + return; + } + + // TODO Create index file for helpers + onMouseEventPreventSideEffects(e); + + return false; + }, + [toggleCollapsed], + ); + + return ( + <> + + {collapsed && ( + + + + )} + + ); +}; + +export default FreePaidDropDown; diff --git a/client/views/admin/apps/components/FreePaidDropDownAnchor.tsx b/client/views/admin/apps/components/FreePaidDropDownAnchor.tsx new file mode 100644 index 000000000000..50a2feef6063 --- /dev/null +++ b/client/views/admin/apps/components/FreePaidDropDownAnchor.tsx @@ -0,0 +1,16 @@ +import { Select } from '@rocket.chat/fuselage'; +import React, { ComponentProps, forwardRef } from 'react'; + +import { FreePaidDropDownGroup } from '../definitions/FreePaidDropDownDefinitions'; + +const FreePaidDropDownAnchor = forwardRef> & { group?: FreePaidDropDownGroup }>( + function FreePaidDropDownAnchor(props, ref) { + const { group } = props; + + const selectedFilter = group?.items.find((item) => item.checked)?.label; + + return