From e7cff82b5d0ba3a5dbdb22f03000568023420b5c Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 6 Oct 2023 16:32:24 -0300 Subject: [PATCH 01/10] chore: add missing `_id` field to `AtLeast` (#30592) --- apps/meteor/lib/callbacks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/lib/callbacks.ts b/apps/meteor/lib/callbacks.ts index 2d683bf27e2a..169144cc2788 100644 --- a/apps/meteor/lib/callbacks.ts +++ b/apps/meteor/lib/callbacks.ts @@ -57,7 +57,7 @@ interface EventLikeCallbackSignatures { 'livechat.afterTakeInquiry': (inq: InquiryWithAgentInfo, agent: { agentId: string; username: string }) => void; 'livechat.afterAgentRemoved': (params: { agent: Pick }) => void; 'afterAddedToRoom': (params: { user: IUser; inviter?: IUser }, room: IRoom) => void; - 'beforeAddedToRoom': (params: { user: AtLeast; inviter: IUser }) => void; + 'beforeAddedToRoom': (params: { user: AtLeast; inviter: IUser }) => void; 'afterCreateDirectRoom': (params: IRoom, second: { members: IUser[]; creatorId: IUser['_id'] }) => void; 'beforeDeleteRoom': (params: IRoom) => void; 'beforeJoinDefaultChannels': (user: IUser) => void; From 3a62ac4eceaafdf33545cf05403b7291c847ef5d Mon Sep 17 00:00:00 2001 From: Guilherme Jun Grillo <48109548+guijun13@users.noreply.github.com> Date: Fri, 6 Oct 2023 17:19:15 -0300 Subject: [PATCH 02/10] fix: user dropdown menu position on RTL layout (#30490) --- .changeset/sweet-feet-relate.md | 5 +++++ apps/meteor/client/sidebar/header/UserMenu.tsx | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 .changeset/sweet-feet-relate.md diff --git a/.changeset/sweet-feet-relate.md b/.changeset/sweet-feet-relate.md new file mode 100644 index 000000000000..f7da740ebcc0 --- /dev/null +++ b/.changeset/sweet-feet-relate.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: user dropdown menu position on RTL layout diff --git a/apps/meteor/client/sidebar/header/UserMenu.tsx b/apps/meteor/client/sidebar/header/UserMenu.tsx index 9fcc7a0d2274..a53836eda311 100644 --- a/apps/meteor/client/sidebar/header/UserMenu.tsx +++ b/apps/meteor/client/sidebar/header/UserMenu.tsx @@ -24,6 +24,7 @@ const UserMenu = ({ user }: { user: IUser }) => { } + placement='bottom-end' selectionMode='multiple' sections={sections} title={t('User_menu')} @@ -36,6 +37,7 @@ const UserMenu = ({ user }: { user: IUser }) => { } medium + placement='bottom-end' selectionMode='multiple' sections={sections} title={t('User_menu')} From 6412ea7c32b8bf0145972f6be2d62eb402ae4fbf Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Fri, 6 Oct 2023 18:17:22 -0300 Subject: [PATCH 03/10] chore: Add required visual indication on `EditRoom` name input (#30589) Co-authored-by: Martin Schoeler Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../client/views/admin/rooms/EditRoom.tsx | 2 +- .../Info/EditRoomInfo/EditChannel.js | 113 +++++++++--------- 2 files changed, 59 insertions(+), 56 deletions(-) diff --git a/apps/meteor/client/views/admin/rooms/EditRoom.tsx b/apps/meteor/client/views/admin/rooms/EditRoom.tsx index c3b772c74748..b12b5e3e1ab9 100644 --- a/apps/meteor/client/views/admin/rooms/EditRoom.tsx +++ b/apps/meteor/client/views/admin/rooms/EditRoom.tsx @@ -225,7 +225,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => )} - {t('Name')} + {t('Name')} diff --git a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js index be7a8b1e4238..22c84fbdebf1 100644 --- a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js +++ b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js @@ -9,6 +9,9 @@ import { Callout, NumberInput, FieldGroup, + FieldLabel, + FieldRow, + FieldHint, Button, ButtonGroup, Box, @@ -309,87 +312,87 @@ function EditChannel({ room, onClickClose, onClickBack }) { - {t('Name')} - + {t('Name')} + - + {canViewDescription && ( - {t('Description')} - + {t('Description')} + - + )} {canViewAnnouncement && ( - {t('Announcement')} - + {t('Announcement')} + - + )} {canViewTopic && ( - {t('Topic')} - + {t('Topic')} + - + )} {canViewType && ( - {t('Private')} - + {t('Private')} + - + - {t('Teams_New_Private_Description_Enabled')} + {t('Teams_New_Private_Description_Enabled')} )} {canViewReadOnly && ( - {t('Read_only')} - + {t('Read_only')} + - + - {t('Only_authorized_users_can_write_new_messages')} + {t('Only_authorized_users_can_write_new_messages')} )} {readOnly && ( - {t('React_when_read_only')} - + {t('React_when_read_only')} + - + - {t('Only_authorized_users_can_react_to_messages')} + {t('Only_authorized_users_can_react_to_messages')} )} {canViewArchived && ( - {t('Room_archivation_state_true')} - + {t('Room_archivation_state_true')} + - + )} {canViewJoinCode && ( - {t('Password_to_access')} - + {t('Password_to_access')} + - + - + - + )} {canViewHideSysMes && ( - {t('Hide_System_Messages')} - + {t('Hide_System_Messages')} + - + - + - + )} {canViewEncrypted && ( - {t('Encrypted')} - + {t('Encrypted')} + - + )} @@ -437,22 +440,22 @@ function EditChannel({ room, onClickClose, onClickBack }) { - {t('RetentionPolicyRoom_Enabled')} - + {t('RetentionPolicyRoom_Enabled')} + - + - {t('RetentionPolicyRoom_OverrideGlobal')} - + {t('RetentionPolicyRoom_OverrideGlobal')} + - + {retentionOverrideGlobal && ( @@ -461,25 +464,25 @@ function EditChannel({ room, onClickClose, onClickBack }) { {t('RetentionPolicyRoom_ReadTheDocs')} - {t('RetentionPolicyRoom_MaxAge', { max: maxAgeDefault })} - + {t('RetentionPolicyRoom_MaxAge', { max: maxAgeDefault })} + - + - {t('RetentionPolicyRoom_ExcludePinned')} - + {t('RetentionPolicyRoom_ExcludePinned')} + - + - {t('RetentionPolicyRoom_FilesOnly')} - + {t('RetentionPolicyRoom_FilesOnly')} + - + From bdc9d8ce27e1c865ea7bd11ab60f9784e1a98e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Fri, 6 Oct 2023 19:29:43 -0300 Subject: [PATCH 04/10] chore: improve message link focus (#30578) --- apps/meteor/client/components/message/MessageContentBody.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/client/components/message/MessageContentBody.tsx b/apps/meteor/client/components/message/MessageContentBody.tsx index 4674528a483f..5552e6da0745 100644 --- a/apps/meteor/client/components/message/MessageContentBody.tsx +++ b/apps/meteor/client/components/message/MessageContentBody.tsx @@ -46,7 +46,7 @@ const MessageContentBody = ({ mentions, channels, md, searchText }: MessageConte text-decoration: underline; } &:focus { - border: 2px solid ${Palette.stroke['stroke-extra-light-highlight']}; + box-shadow: 0 0 0 2px ${Palette.stroke['stroke-extra-light-highlight']}; border-radius: 2px; } } From a8718eddc03cda4907e6c20de151ec9e982976cd Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 6 Oct 2023 20:29:39 -0300 Subject: [PATCH 05/10] feat: New permission to kick users (#30535) Co-authored-by: Guilherme Gazzo <5263975+ggazzo@users.noreply.github.com> --- .changeset/seven-carpets-march.md | 5 ++ apps/meteor/app/api/server/v1/groups.ts | 74 ++++++++++--------- .../server/constant/permissions.ts | 2 + .../rocketchat-i18n/i18n/en.i18n.json | 4 + apps/meteor/server/lib/migrations.ts | 27 +++++-- .../server/methods/removeUserFromRoom.ts | 28 ++++--- apps/meteor/server/startup/migrations/xrun.js | 6 +- .../core-typings/src/migrations/IControl.ts | 1 + 8 files changed, 96 insertions(+), 51 deletions(-) create mode 100644 .changeset/seven-carpets-march.md diff --git a/.changeset/seven-carpets-march.md b/.changeset/seven-carpets-march.md new file mode 100644 index 000000000000..46fd1b7ddb62 --- /dev/null +++ b/.changeset/seven-carpets-march.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Add new permission to allow kick users from rooms without being a member diff --git a/apps/meteor/app/api/server/v1/groups.ts b/apps/meteor/app/api/server/v1/groups.ts index ef18d4256348..8f2999cee71e 100644 --- a/apps/meteor/app/api/server/v1/groups.ts +++ b/apps/meteor/app/api/server/v1/groups.ts @@ -26,29 +26,7 @@ import { getLoggedInUser } from '../helpers/getLoggedInUser'; import { getPaginationItems } from '../helpers/getPaginationItems'; import { getUserFromParams, getUserListFromParams } from '../helpers/getUserFromParams'; -// Returns the private group subscription IF found otherwise it will return the failure of why it didn't. Check the `statusCode` property -async function findPrivateGroupByIdOrName({ - params, - checkedArchived = true, - userId, -}: { - params: - | { - roomId?: string; - } - | { - roomName?: string; - }; - userId: string; - checkedArchived?: boolean; -}): Promise<{ - rid: string; - open: boolean; - ro: boolean; - t: string; - name: string; - broadcast: boolean; -}> { +async function getRoomFromParams(params: { roomId?: string } | { roomName?: string }): Promise { if ( (!('roomId' in params) && !('roomName' in params)) || ('roomId' in params && !(params as { roomId?: string }).roomId && 'roomName' in params && !(params as { roomName?: string }).roomName) @@ -68,17 +46,48 @@ async function findPrivateGroupByIdOrName({ broadcast: 1, }, }; - let room: IRoom | null = null; - if ('roomId' in params) { - room = await Rooms.findOneById(params.roomId || '', roomOptions); - } else if ('roomName' in params) { - room = await Rooms.findOneByName(params.roomName || '', roomOptions); - } + + const room = await (() => { + if ('roomId' in params) { + return Rooms.findOneById(params.roomId || '', roomOptions); + } + if ('roomName' in params) { + return Rooms.findOneByName(params.roomName || '', roomOptions); + } + })(); if (!room || room.t !== 'p') { throw new Meteor.Error('error-room-not-found', 'The required "roomId" or "roomName" param provided does not match any group'); } + return room; +} + +// Returns the private group subscription IF found otherwise it will return the failure of why it didn't. Check the `statusCode` property +async function findPrivateGroupByIdOrName({ + params, + checkedArchived = true, + userId, +}: { + params: + | { + roomId?: string; + } + | { + roomName?: string; + }; + userId: string; + checkedArchived?: boolean; +}): Promise<{ + rid: string; + open: boolean; + ro: boolean; + t: string; + name: string; + broadcast: boolean; +}> { + const room = await getRoomFromParams(params); + const user = await Users.findOneById(userId, { projections: { username: 1 } }); if (!room || !user || !(await canAccessRoomAsync(room, user))) { @@ -585,17 +594,14 @@ API.v1.addRoute( { authRequired: true }, { async post() { - const findResult = await findPrivateGroupByIdOrName({ - params: this.bodyParams, - userId: this.userId, - }); + const room = await getRoomFromParams(this.bodyParams); const user = await getUserFromParams(this.bodyParams); if (!user?.username) { return API.v1.failure('Invalid user'); } - await removeUserFromRoomMethod(this.userId, { rid: findResult.rid, username: user.username }); + await removeUserFromRoomMethod(this.userId, { rid: room._id, username: user.username }); return API.v1.success(); }, diff --git a/apps/meteor/app/authorization/server/constant/permissions.ts b/apps/meteor/app/authorization/server/constant/permissions.ts index fc917028c33f..7b5f1594e5c3 100644 --- a/apps/meteor/app/authorization/server/constant/permissions.ts +++ b/apps/meteor/app/authorization/server/constant/permissions.ts @@ -10,6 +10,8 @@ export const permissions = [ { _id: 'add-user-to-joined-room', roles: ['admin', 'owner', 'moderator'] }, { _id: 'add-user-to-any-c-room', roles: ['admin'] }, { _id: 'add-user-to-any-p-room', roles: [] }, + { _id: 'kick-user-from-any-c-room', roles: ['admin'] }, + { _id: 'kick-user-from-any-p-room', roles: [] }, { _id: 'api-bypass-rate-limit', roles: ['admin', 'bot', 'app'] }, { _id: 'archive-room', roles: ['admin', 'owner'] }, { _id: 'assign-admin-role', roles: ['admin'] }, diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index e713f095f490..91993ff443fb 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -2762,6 +2762,10 @@ "Jump_to_message": "Jump to message", "Jump_to_recent_messages": "Jump to recent messages", "Just_invited_people_can_access_this_channel": "Just invited people can access this channel.", + "kick-user-from-any-c-room": "Kick User from Any Public Channel", + "kick-user-from-any-c-room_description": "Permission to kick a user from any public channel", + "kick-user-from-any-p-room": "Kick User from Any Private Channel", + "kick-user-from-any-p-room_description": "Permission to kick a user from any private channel", "Katex_Dollar_Syntax": "Allow Dollar Syntax", "Katex_Dollar_Syntax_Description": "Allow using $$katex block$$ and $inline katex$ syntaxes", "Katex_Enabled": "Katex Enabled", diff --git a/apps/meteor/server/lib/migrations.ts b/apps/meteor/server/lib/migrations.ts index da3aeec761e6..f70b5bcca9ff 100644 --- a/apps/meteor/server/lib/migrations.ts +++ b/apps/meteor/server/lib/migrations.ts @@ -292,9 +292,24 @@ export async function migrateDatabase(targetVersion: 'latest' | number, subcomma return true; } -export const onFreshInstall = - (await getControl()).version !== 0 - ? async (): Promise => { - /* noop */ - } - : (fn: () => unknown): unknown => fn(); +export async function onServerVersionChange(cb: () => Promise): Promise { + const result = await Migrations.findOneAndUpdate( + { + _id: 'upgrade', + }, + { + $set: { + hash: Info.commit.hash, + }, + }, + { + upsert: true, + }, + ); + + if (result.value?.hash === Info.commit.hash) { + return; + } + + await cb(); +} diff --git a/apps/meteor/server/methods/removeUserFromRoom.ts b/apps/meteor/server/methods/removeUserFromRoom.ts index ea5bfa9edcff..2f29b1f55039 100644 --- a/apps/meteor/server/methods/removeUserFromRoom.ts +++ b/apps/meteor/server/methods/removeUserFromRoom.ts @@ -4,7 +4,7 @@ import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; -import { getUsersInRole } from '../../app/authorization/server'; +import { canAccessRoomAsync, getUsersInRole } from '../../app/authorization/server'; import { hasPermissionAsync } from '../../app/authorization/server/functions/hasPermission'; import { hasRoleAsync } from '../../app/authorization/server/functions/hasRole'; import { RoomMemberActions } from '../../definition/IRoomTypeConfig'; @@ -35,8 +35,6 @@ export const removeUserFromRoomMethod = async (fromId: string, data: { rid: stri }); } - const removedUser = await Users.findOneByUsernameIgnoringCase(data.username); - const fromUser = await Users.findOneById(fromId); if (!fromUser) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { @@ -44,13 +42,25 @@ export const removeUserFromRoomMethod = async (fromId: string, data: { rid: stri }); } - const subscription = await Subscriptions.findOneByRoomIdAndUserId(data.rid, removedUser._id, { - projection: { _id: 1 }, - }); - if (!subscription) { - throw new Meteor.Error('error-user-not-in-room', 'User is not in this room', { - method: 'removeUserFromRoom', + // did this way so a ctrl-f would find the permission being used + const kickAnyUserPermission = room.t === 'c' ? 'kick-user-from-any-c-room' : 'kick-user-from-any-p-room'; + + const canKickAnyUser = await hasPermissionAsync(fromId, kickAnyUserPermission); + if (!canKickAnyUser && !(await canAccessRoomAsync(room, fromUser))) { + throw new Meteor.Error('error-room-not-found', 'The required "roomId" or "roomName" param provided does not match any group'); + } + + const removedUser = await Users.findOneByUsernameIgnoringCase(data.username); + + if (!canKickAnyUser) { + const subscription = await Subscriptions.findOneByRoomIdAndUserId(data.rid, removedUser._id, { + projection: { _id: 1 }, }); + if (!subscription) { + throw new Meteor.Error('error-user-not-in-room', 'User is not in this room', { + method: 'removeUserFromRoom', + }); + } } if (await hasRoleAsync(removedUser._id, 'owner', room._id)) { diff --git a/apps/meteor/server/startup/migrations/xrun.js b/apps/meteor/server/startup/migrations/xrun.js index bd3d19a7cbee..1af7cb8ad8ad 100644 --- a/apps/meteor/server/startup/migrations/xrun.js +++ b/apps/meteor/server/startup/migrations/xrun.js @@ -1,9 +1,11 @@ import { upsertPermissions } from '../../../app/authorization/server/functions/upsertPermissions'; -import { migrateDatabase, onFreshInstall } from '../../lib/migrations'; +import { migrateDatabase, onServerVersionChange } from '../../lib/migrations'; const { MIGRATION_VERSION = 'latest' } = process.env; const [version, ...subcommands] = MIGRATION_VERSION.split(','); await migrateDatabase(version === 'latest' ? version : parseInt(version), subcommands); -await onFreshInstall(upsertPermissions); + +// if the server is starting with a different version we update the permissions +await onServerVersionChange(() => upsertPermissions()); diff --git a/packages/core-typings/src/migrations/IControl.ts b/packages/core-typings/src/migrations/IControl.ts index 9ff993703550..3f89ce730f1a 100644 --- a/packages/core-typings/src/migrations/IControl.ts +++ b/packages/core-typings/src/migrations/IControl.ts @@ -2,6 +2,7 @@ export type IControl = { _id: string; version: number; locked: boolean; + hash?: string; buildAt?: string | Date; lockedAt?: string | Date; }; From 365e5fe3312925e4319ae2b5b84fc7c73601547f Mon Sep 17 00:00:00 2001 From: Martin Schoeler Date: Mon, 9 Oct 2023 06:58:24 -0300 Subject: [PATCH 06/10] fix: don't save the filter value on offlineMessageChannelName (#30576) --- .../omnichannel/departments/EditDepartment.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx b/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx index 46919307b052..bb1d5b057437 100644 --- a/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx +++ b/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx @@ -15,10 +15,10 @@ import { Button, PaginatedSelectFiltered, } from '@rocket.chat/fuselage'; -import { useMutableCallback, useUniqueId } from '@rocket.chat/fuselage-hooks'; +import { useDebouncedValue, useMutableCallback, useUniqueId } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useRoute, useMethod, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; import { useQueryClient } from '@tanstack/react-query'; -import React, { useMemo } from 'react'; +import React, { useMemo, useState } from 'react'; import { Controller, useForm } from 'react-hook-form'; import { validateEmail } from '../../../../lib/emailValidator'; @@ -130,10 +130,13 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen } = useForm({ mode: 'onChange', defaultValues: initialValues }); const requestTagBeforeClosingChat = watch('requestTagBeforeClosingChat'); - const offlineMessageChannelName = watch('offlineMessageChannelName'); + + const [fallbackFilter, setFallbackFilter] = useState(''); + + const debouncedFallbackFilter = useDebouncedValue(fallbackFilter, 500); const { itemsList: RoomsList, loadMoreItems: loadMoreRooms } = useRoomsList( - useMemo(() => ({ text: offlineMessageChannelName }), [offlineMessageChannelName]), + useMemo(() => ({ text: debouncedFallbackFilter }), [debouncedFallbackFilter]), ); const { phase: roomsPhase, items: roomsItems, itemCount: roomsTotal } = useRecordList(RoomsList); @@ -324,13 +327,14 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen value={value} onChange={onChange} flexShrink={0} - filter={value} - setFilter={onChange} + filter={fallbackFilter} + setFilter={setFallbackFilter as (value?: string | number) => void} options={roomsItems} placeholder={t('Channel_name')} endReached={ roomsPhase === AsyncStatePhase.LOADING ? () => undefined : (start) => loadMoreRooms(start, Math.min(50, roomsTotal)) } + aria-busy={fallbackFilter !== debouncedFallbackFilter} /> )} /> From 0ab30f88e628b38ea12e5d0475d2edb47a866d34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrique=20Guimar=C3=A3es=20Ribeiro?= Date: Mon, 9 Oct 2023 14:06:22 -0300 Subject: [PATCH 07/10] chore: Change Records page name to Reports (#30513) --- apps/meteor/client/views/admin/routes.tsx | 6 +++--- apps/meteor/client/views/admin/sidebarItems.ts | 4 ++-- .../meteor/client/views/admin/viewLogs/AnalyticsReports.tsx | 5 ++++- apps/meteor/client/views/admin/viewLogs/ViewLogsPage.tsx | 2 +- apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json | 6 +++--- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/apps/meteor/client/views/admin/routes.tsx b/apps/meteor/client/views/admin/routes.tsx index a25bea5affaa..bea10777d66b 100644 --- a/apps/meteor/client/views/admin/routes.tsx +++ b/apps/meteor/client/views/admin/routes.tsx @@ -70,8 +70,8 @@ declare module '@rocket.chat/ui-contexts' { pattern: '/admin/registration/:page?'; }; 'admin-view-logs': { - pathname: '/admin/records'; - pattern: '/admin/records'; + pathname: '/admin/reports'; + pattern: '/admin/reports'; }; 'federation-dashboard': { pathname: '/admin/federation'; @@ -193,7 +193,7 @@ registerAdminRoute('/registration/:page?', { component: lazy(() => import('./cloud/CloudRoute')), }); -registerAdminRoute('/records', { +registerAdminRoute('/reports', { name: 'admin-view-logs', component: lazy(() => import('./viewLogs/ViewLogsRoute')), }); diff --git a/apps/meteor/client/views/admin/sidebarItems.ts b/apps/meteor/client/views/admin/sidebarItems.ts index 50a3284b5ed1..2beee76cee02 100644 --- a/apps/meteor/client/views/admin/sidebarItems.ts +++ b/apps/meteor/client/views/admin/sidebarItems.ts @@ -112,8 +112,8 @@ export const { permissionGranted: (): boolean => hasPermission('run-import'), }, { - href: '/admin/records', - i18nLabel: 'Records', + href: '/admin/reports', + i18nLabel: 'Reports', icon: 'post', permissionGranted: (): boolean => hasPermission('view-logs'), }, diff --git a/apps/meteor/client/views/admin/viewLogs/AnalyticsReports.tsx b/apps/meteor/client/views/admin/viewLogs/AnalyticsReports.tsx index 7771298ceb73..cd300c14a481 100644 --- a/apps/meteor/client/views/admin/viewLogs/AnalyticsReports.tsx +++ b/apps/meteor/client/views/admin/viewLogs/AnalyticsReports.tsx @@ -18,7 +18,10 @@ const AnalyticsReports = () => { {t('How_and_why_we_collect_usage_data')} - {t('Analytics_page_briefing')} + + {t('Analytics_page_briefing_first_paragraph')} + + {t('Analytics_page_briefing_second_paragraph')} {isSuccess &&
{JSON.stringify(data, null, '\t')}
} diff --git a/apps/meteor/client/views/admin/viewLogs/ViewLogsPage.tsx b/apps/meteor/client/views/admin/viewLogs/ViewLogsPage.tsx index 4463fec8f5bf..2c1613f3ef74 100644 --- a/apps/meteor/client/views/admin/viewLogs/ViewLogsPage.tsx +++ b/apps/meteor/client/views/admin/viewLogs/ViewLogsPage.tsx @@ -14,7 +14,7 @@ const ViewLogsPage = (): ReactElement => { return ( - + setTab('Logs')} selected={tab === 'Logs'}> diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 91993ff443fb..b1ac03bd7df7 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -424,7 +424,8 @@ "Analytics_features_users_Description": "Tracks custom events related to actions related to users (password reset times, profile picture change, etc).", "Analytics_Google": "Google Analytics", "Analytics_Google_id": "Tracking ID", - "Analytics_page_briefing": "Rocket.Chat collects anonymous usage data to identify how many instances are deployed and to improve the product for all users. We take your privacy seriously, so the usage data is encrypted and stored securely.", + "Analytics_page_briefing_first_paragraph": "Rocket.Chat collects anonymous usage data, such as feature usage and session lengths, to improve the product for everyone.", + "Analytics_page_briefing_second_paragraph": "We protect your privacy by never collecting personal or sensitive data. This section shows what is collected, reinforcing our commitment to transparency and trust.", "Analyze_practical_usage": "Analyze practical usage statistics about users, messages and channels", "and": "and", "And_more": "And {{length}} more", @@ -2484,7 +2485,7 @@ "Hospitality_Businness": "Hospitality Business", "hours": "hours", "Hours": "Hours", - "How_and_why_we_collect_usage_data": "How and why we collect usage data", + "How_and_why_we_collect_usage_data": "How and why usage data is collected", "How_friendly_was_the_chat_agent": "How friendly was the chat agent?", "How_knowledgeable_was_the_chat_agent": "How knowledgeable was the chat agent?", "How_long_to_wait_after_agent_goes_offline": "How Long to Wait After Agent Goes Offline", @@ -4182,7 +4183,6 @@ "Receive_Login_Detection_Emails_Description": "Receive an email each time a new login is detected on your account.", "Recent_Import_History": "Recent Import History", "Record": "Record", - "Records": "Records", "recording": "recording", "Redirect_URI": "Redirect URI", "Redirect_URL_does_not_match": "Redirect URL does not match", From a58f2a707b6f0933f7ecd263745ab3d0c49d0027 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Mon, 9 Oct 2023 11:21:09 -0600 Subject: [PATCH 08/10] regression: Undefined MAC count on startup cloud sync (#30605) --- apps/meteor/app/cloud/server/functions/buildRegistrationData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts b/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts index f887c9e6395c..c2bd91e82dd8 100644 --- a/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts +++ b/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts @@ -89,7 +89,7 @@ export async function buildWorkspaceRegistrationData Date: Mon, 9 Oct 2023 14:33:45 -0300 Subject: [PATCH 09/10] chore: Improve cache of static files (#30290) --- .changeset/wicked-humans-hang.md | 5 +++ apps/meteor/app/cors/server/cors.ts | 41 +++++++++++++++++++ .../custom-sounds/client/lib/CustomSounds.ts | 29 ++++++------- apps/meteor/app/ui-master/server/inject.ts | 24 +++++++---- apps/meteor/app/utils/client/getURL.ts | 6 +++ 5 files changed, 84 insertions(+), 21 deletions(-) create mode 100644 .changeset/wicked-humans-hang.md diff --git a/.changeset/wicked-humans-hang.md b/.changeset/wicked-humans-hang.md new file mode 100644 index 000000000000..e793bc978902 --- /dev/null +++ b/.changeset/wicked-humans-hang.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Improve cache of static files diff --git a/apps/meteor/app/cors/server/cors.ts b/apps/meteor/app/cors/server/cors.ts index 03a42e45a17b..cb6fa94273a2 100644 --- a/apps/meteor/app/cors/server/cors.ts +++ b/apps/meteor/app/cors/server/cors.ts @@ -1,4 +1,6 @@ +import { createHash } from 'crypto'; import type http from 'http'; +import type { UrlWithParsedQuery } from 'url'; import url from 'url'; import { Logger } from '@rocket.chat/logger'; @@ -77,6 +79,19 @@ WebApp.rawConnectHandlers.use((_req: http.IncomingMessage, res: http.ServerRespo }); const _staticFilesMiddleware = WebAppInternals.staticFilesMiddleware; +declare module 'meteor/webapp' { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace WebApp { + function categorizeRequest( + req: http.IncomingMessage, + ): { arch: string; path: string; url: UrlWithParsedQuery } & Record; + } +} + +// These routes already handle cache control on their own +const cacheControlledRoutes: Array = ['/assets', '/custom-sounds', '/emoji-custom', '/avatar', '/file-upload'].map( + (route) => new RegExp(`^${route}`, 'i'), +); // @ts-expect-error - accessing internal property of webapp WebAppInternals.staticFilesMiddleware = function ( @@ -86,6 +101,32 @@ WebAppInternals.staticFilesMiddleware = function ( next: NextFunction, ) { res.setHeader('Access-Control-Allow-Origin', '*'); + const { arch, path, url } = WebApp.categorizeRequest(req); + + if (Meteor.isProduction && !cacheControlledRoutes.some((regexp) => regexp.test(path))) { + res.setHeader('Cache-Control', 'public, max-age=31536000'); + } + + // Prevent meteor_runtime_config.js to load from a different expected hash possibly causing + // a cache of the file for the wrong hash and start a client loop due to the mismatch + // of the hashes of ui versions which would be checked against a websocket response + if (path === '/meteor_runtime_config.js') { + const program = WebApp.clientPrograms[arch] as (typeof WebApp.clientPrograms)[string] & { + meteorRuntimeConfigHash?: string; + meteorRuntimeConfig: string; + }; + + if (!program?.meteorRuntimeConfigHash) { + program.meteorRuntimeConfigHash = createHash('sha1') + .update(JSON.stringify(encodeURIComponent(program.meteorRuntimeConfig))) + .digest('hex'); + } + + if (program.meteorRuntimeConfigHash !== url.query.hash) { + res.writeHead(404); + return res.end(); + } + } return _staticFilesMiddleware(staticFiles, req, res, next); }; diff --git a/apps/meteor/app/custom-sounds/client/lib/CustomSounds.ts b/apps/meteor/app/custom-sounds/client/lib/CustomSounds.ts index a4f59136a1f9..f881c15f9886 100644 --- a/apps/meteor/app/custom-sounds/client/lib/CustomSounds.ts +++ b/apps/meteor/app/custom-sounds/client/lib/CustomSounds.ts @@ -7,21 +7,22 @@ import { getURL } from '../../../utils/client'; import { sdk } from '../../../utils/client/lib/SDKClient'; const getCustomSoundId = (soundId: ICustomSound['_id']) => `custom-sound-${soundId}`; +const getAssetUrl = (asset: string, params?: Record) => getURL(asset, params, undefined, true); const defaultSounds = [ - { _id: 'chime', name: 'Chime', extension: 'mp3', src: getURL('sounds/chime.mp3') }, - { _id: 'door', name: 'Door', extension: 'mp3', src: getURL('sounds/door.mp3') }, - { _id: 'beep', name: 'Beep', extension: 'mp3', src: getURL('sounds/beep.mp3') }, - { _id: 'chelle', name: 'Chelle', extension: 'mp3', src: getURL('sounds/chelle.mp3') }, - { _id: 'ding', name: 'Ding', extension: 'mp3', src: getURL('sounds/ding.mp3') }, - { _id: 'droplet', name: 'Droplet', extension: 'mp3', src: getURL('sounds/droplet.mp3') }, - { _id: 'highbell', name: 'Highbell', extension: 'mp3', src: getURL('sounds/highbell.mp3') }, - { _id: 'seasons', name: 'Seasons', extension: 'mp3', src: getURL('sounds/seasons.mp3') }, - { _id: 'telephone', name: 'Telephone', extension: 'mp3', src: getURL('sounds/telephone.mp3') }, - { _id: 'outbound-call-ringing', name: 'Outbound Call Ringing', extension: 'mp3', src: getURL('sounds/outbound-call-ringing.mp3') }, - { _id: 'call-ended', name: 'Call Ended', extension: 'mp3', src: getURL('sounds/call-ended.mp3') }, - { _id: 'dialtone', name: 'Dialtone', extension: 'mp3', src: getURL('sounds/dialtone.mp3') }, - { _id: 'ringtone', name: 'Ringtone', extension: 'mp3', src: getURL('sounds/ringtone.mp3') }, + { _id: 'chime', name: 'Chime', extension: 'mp3', src: getAssetUrl('sounds/chime.mp3') }, + { _id: 'door', name: 'Door', extension: 'mp3', src: getAssetUrl('sounds/door.mp3') }, + { _id: 'beep', name: 'Beep', extension: 'mp3', src: getAssetUrl('sounds/beep.mp3') }, + { _id: 'chelle', name: 'Chelle', extension: 'mp3', src: getAssetUrl('sounds/chelle.mp3') }, + { _id: 'ding', name: 'Ding', extension: 'mp3', src: getAssetUrl('sounds/ding.mp3') }, + { _id: 'droplet', name: 'Droplet', extension: 'mp3', src: getAssetUrl('sounds/droplet.mp3') }, + { _id: 'highbell', name: 'Highbell', extension: 'mp3', src: getAssetUrl('sounds/highbell.mp3') }, + { _id: 'seasons', name: 'Seasons', extension: 'mp3', src: getAssetUrl('sounds/seasons.mp3') }, + { _id: 'telephone', name: 'Telephone', extension: 'mp3', src: getAssetUrl('sounds/telephone.mp3') }, + { _id: 'outbound-call-ringing', name: 'Outbound Call Ringing', extension: 'mp3', src: getAssetUrl('sounds/outbound-call-ringing.mp3') }, + { _id: 'call-ended', name: 'Call Ended', extension: 'mp3', src: getAssetUrl('sounds/call-ended.mp3') }, + { _id: 'dialtone', name: 'Dialtone', extension: 'mp3', src: getAssetUrl('sounds/dialtone.mp3') }, + { _id: 'ringtone', name: 'Ringtone', extension: 'mp3', src: getAssetUrl('sounds/ringtone.mp3') }, ]; class CustomSoundsClass { @@ -85,7 +86,7 @@ class CustomSoundsClass { } getURL(sound: ICustomSound) { - return getURL(`/custom-sounds/${sound._id}.${sound.extension}?_dc=${sound.random || 0}`); + return getAssetUrl(`/custom-sounds/${sound._id}.${sound.extension}`, { _dc: sound.random || 0 }); } getList() { diff --git a/apps/meteor/app/ui-master/server/inject.ts b/apps/meteor/app/ui-master/server/inject.ts index 78112bcee343..1e00a0e47433 100644 --- a/apps/meteor/app/ui-master/server/inject.ts +++ b/apps/meteor/app/ui-master/server/inject.ts @@ -32,7 +32,7 @@ const callback: NextHandleFunction = (req, res, next) => { return; } - const injection = headInjections.get(pathname.replace(/^\//, '')) as Injection | undefined; + const injection = headInjections.get(pathname.replace(/^\//, '').split('_')[0]) as Injection | undefined; if (!injection || typeof injection === 'string') { next(); @@ -76,27 +76,37 @@ export const injectIntoHead = (key: string, value: Injection): void => { }; export const addScript = (key: string, content: string): void => { + if (/_/.test(key)) { + throw new Error('inject.js > addScript - key cannot contain "_" (underscore)'); + } + if (!content.trim()) { - injectIntoHead(`${key}.js`, ''); + injectIntoHead(key, ''); return; } const currentHash = crypto.createHash('sha1').update(content).digest('hex'); - injectIntoHead(`${key}.js`, { + + injectIntoHead(key, { type: 'JS', - tag: ``, + tag: ``, content, }); }; export const addStyle = (key: string, content: string): void => { + if (/_/.test(key)) { + throw new Error('inject.js > addStyle - key cannot contain "_" (underscore)'); + } + if (!content.trim()) { - injectIntoHead(`${key}.css`, ''); + injectIntoHead(key, ''); return; } const currentHash = crypto.createHash('sha1').update(content).digest('hex'); - injectIntoHead(`${key}.css`, { + + injectIntoHead(key, { type: 'CSS', - tag: ``, + tag: ``, content, }); }; diff --git a/apps/meteor/app/utils/client/getURL.ts b/apps/meteor/app/utils/client/getURL.ts index 91ef0989bd19..040b6dfa9dc2 100644 --- a/apps/meteor/app/utils/client/getURL.ts +++ b/apps/meteor/app/utils/client/getURL.ts @@ -1,13 +1,19 @@ import { settings } from '../../settings/client'; import { getURLWithoutSettings } from '../lib/getURL'; +import { Info } from '../rocketchat.info'; export const getURL = function ( path: string, // eslint-disable-next-line @typescript-eslint/naming-convention params: Record = {}, cloudDeepLinkUrl?: string, + cacheKey?: boolean, ): string { const cdnPrefix = settings.get('CDN_PREFIX') || ''; const siteUrl = settings.get('Site_Url') || ''; + if (cacheKey) { + path += `${path.includes('?') ? '&' : '?'}cacheKey=${Info.version}`; + } + return getURLWithoutSettings(path, params, cdnPrefix, siteUrl, cloudDeepLinkUrl); }; From 06a8e302890c15b29c29a483d5eab8675f0447dc Mon Sep 17 00:00:00 2001 From: csuadev <72958726+csuadev@users.noreply.github.com> Date: Mon, 9 Oct 2023 13:25:57 -0500 Subject: [PATCH 10/10] chore: Change plan name Enterprise to Premium on marketplace (#30487) --- .changeset/odd-hounds-thank.md | 5 +++++ .../GenericUpsellModal/GenericUpsellModal.tsx | 2 +- .../views/marketplace/AppsPage/AppsFilters.tsx | 2 +- .../marketplace/AppsPage/AppsPageContent.tsx | 4 ++-- .../client/views/marketplace/BundleChips.tsx | 8 ++++---- .../marketplace/components/EnabledAppsCount.tsx | 2 +- .../marketplace/components/MarketplaceHeader.tsx | 2 +- .../client/views/marketplace/hooks/useAppInfo.ts | 2 +- .../views/marketplace/hooks/useAppsCountQuery.ts | 4 ++-- .../views/marketplace/hooks/useFilteredApps.ts | 4 ++-- .../client/views/marketplace/sidebarItems.tsx | 4 ++-- .../packages/rocketchat-i18n/i18n/en.i18n.json | 16 ++++++++++------ 12 files changed, 32 insertions(+), 23 deletions(-) create mode 100644 .changeset/odd-hounds-thank.md diff --git a/.changeset/odd-hounds-thank.md b/.changeset/odd-hounds-thank.md new file mode 100644 index 000000000000..aaddc5d51a38 --- /dev/null +++ b/.changeset/odd-hounds-thank.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +chore: Change plan name Enterprise to Premium on marketplace diff --git a/apps/meteor/client/components/GenericUpsellModal/GenericUpsellModal.tsx b/apps/meteor/client/components/GenericUpsellModal/GenericUpsellModal.tsx index ec9f852c8a8b..513b31dd81fa 100644 --- a/apps/meteor/client/components/GenericUpsellModal/GenericUpsellModal.tsx +++ b/apps/meteor/client/components/GenericUpsellModal/GenericUpsellModal.tsx @@ -42,7 +42,7 @@ const GenericUpsellModal = ({ {icon && } - {tagline ?? t('Enterprise_capability')} + {tagline ?? t('Premium_capability')} {title} diff --git a/apps/meteor/client/views/marketplace/AppsPage/AppsFilters.tsx b/apps/meteor/client/views/marketplace/AppsPage/AppsFilters.tsx index 2fdbffda7d4d..f1332284c97f 100644 --- a/apps/meteor/client/views/marketplace/AppsPage/AppsFilters.tsx +++ b/apps/meteor/client/views/marketplace/AppsPage/AppsFilters.tsx @@ -50,7 +50,7 @@ const AppsFilters = ({ const appsSearchPlaceholders: { [key: string]: string } = { explore: t('Search_Apps'), - enterprise: t('Search_Enterprise_Apps'), + enterprise: t('Search_Premium_Apps'), installed: t('Search_Installed_Apps'), requested: t('Search_Requested_Apps'), private: t('Search_Private_apps'), diff --git a/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx b/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx index 5b47634bff29..1bc7642e5afc 100644 --- a/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx +++ b/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx @@ -40,7 +40,7 @@ const AppsPageContent = (): ReactElement => { { id: 'all', label: t('All_Prices'), checked: true }, { id: 'free', label: t('Free_Apps'), checked: false }, { id: 'paid', label: t('Paid_Apps'), checked: false }, - { id: 'enterprise', label: t('Enterprise'), checked: false }, + { id: 'premium', label: t('Premium'), checked: false }, ], }); const freePaidFilterOnSelected = useRadioToggle(setFreePaidFilterStructure); @@ -89,7 +89,7 @@ const AppsPageContent = (): ReactElement => { const getAppsData = useCallback((): appsDataType => { switch (context) { - case 'enterprise': + case 'premium': case 'explore': case 'requested': return marketplaceApps; diff --git a/apps/meteor/client/views/marketplace/BundleChips.tsx b/apps/meteor/client/views/marketplace/BundleChips.tsx index 9f988534fe14..4e2953bd8519 100644 --- a/apps/meteor/client/views/marketplace/BundleChips.tsx +++ b/apps/meteor/client/views/marketplace/BundleChips.tsx @@ -18,15 +18,15 @@ const BundleChips = ({ bundledIn }: BundleChipsProps): ReactElement => { return ( <> - {bundledIn.map((bundle) => ( + {bundledIn.map(({ bundleId, bundleName }) => ( - {bundle.bundleName} + {bundleName} ))} diff --git a/apps/meteor/client/views/marketplace/components/EnabledAppsCount.tsx b/apps/meteor/client/views/marketplace/components/EnabledAppsCount.tsx index 72cfa5474346..da135cbdedfc 100644 --- a/apps/meteor/client/views/marketplace/components/EnabledAppsCount.tsx +++ b/apps/meteor/client/views/marketplace/components/EnabledAppsCount.tsx @@ -15,7 +15,7 @@ const EnabledAppsCount = ({ percentage: number; limit: number; enabled: number; - context: 'private' | 'explore' | 'installed' | 'enterprise' | 'requested'; + context: 'private' | 'explore' | 'installed' | 'premium' | 'requested'; }): ReactElement | null => { const t = useTranslation(); diff --git a/apps/meteor/client/views/marketplace/components/MarketplaceHeader.tsx b/apps/meteor/client/views/marketplace/components/MarketplaceHeader.tsx index f93cb1fcd339..7696801c3124 100644 --- a/apps/meteor/client/views/marketplace/components/MarketplaceHeader.tsx +++ b/apps/meteor/client/views/marketplace/components/MarketplaceHeader.tsx @@ -12,7 +12,7 @@ import EnabledAppsCount from './EnabledAppsCount'; const MarketplaceHeader = ({ title }: { title: string }): ReactElement | null => { const t = useTranslation(); const isAdmin = usePermission('manage-apps'); - const context = (useRouteParameter('context') || 'explore') as 'private' | 'explore' | 'installed' | 'enterprise' | 'requested'; + const context = (useRouteParameter('context') || 'explore') as 'private' | 'explore' | 'installed' | 'premium' | 'requested'; const route = useRoute('marketplace'); const setModal = useSetModal(); const result = useAppsCountQuery(context); diff --git a/apps/meteor/client/views/marketplace/hooks/useAppInfo.ts b/apps/meteor/client/views/marketplace/hooks/useAppInfo.ts index 7e69cdeef97c..44ab240ce7b3 100644 --- a/apps/meteor/client/views/marketplace/hooks/useAppInfo.ts +++ b/apps/meteor/client/views/marketplace/hooks/useAppInfo.ts @@ -36,7 +36,7 @@ export const useAppInfo = (appId: string, context: string): AppInfo | undefined } let appResult: App | undefined; - const marketplaceAppsContexts = ['explore', 'enterprise', 'requested']; + const marketplaceAppsContexts = ['explore', 'premium', 'requested']; if (marketplaceAppsContexts.includes(context)) appResult = marketplaceApps.value?.apps.find((app) => app.id === appId); diff --git a/apps/meteor/client/views/marketplace/hooks/useAppsCountQuery.ts b/apps/meteor/client/views/marketplace/hooks/useAppsCountQuery.ts index 10689c773479..a571eac3b71f 100644 --- a/apps/meteor/client/views/marketplace/hooks/useAppsCountQuery.ts +++ b/apps/meteor/client/views/marketplace/hooks/useAppsCountQuery.ts @@ -11,10 +11,10 @@ const getProgressBarValues = (numberOfEnabledApps: number, enabledAppsLimit: num percentage: Math.round((numberOfEnabledApps / enabledAppsLimit) * 100), }); -export type MarketplaceRouteContext = 'private' | 'explore' | 'installed' | 'enterprise' | 'requested'; +export type MarketplaceRouteContext = 'private' | 'explore' | 'installed' | 'premium' | 'requested'; export function isMarketplaceRouteContext(context: string): context is MarketplaceRouteContext { - return ['private', 'explore', 'installed', 'enterprise', 'requested'].includes(context); + return ['private', 'explore', 'installed', 'premium', 'requested'].includes(context); } export const useAppsCountQuery = (context: MarketplaceRouteContext) => { diff --git a/apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts b/apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts index 1027aae75a8a..221990f7af2a 100644 --- a/apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts +++ b/apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts @@ -66,7 +66,7 @@ export const useFilteredApps = ({ const filterByPurchaseType: Record App[]> = { all: fallback, paid: (apps: App[]) => apps.filter(filterAppsByPaid), - enterprise: (apps: App[]) => apps.filter(filterAppsByEnterprise), + premium: (apps: App[]) => apps.filter(filterAppsByEnterprise), free: (apps: App[]) => apps.filter(filterAppsByFree), }; @@ -80,7 +80,7 @@ export const useFilteredApps = ({ explore: fallback, installed: fallback, private: fallback, - enterprise: (apps: App[]) => apps.filter(({ categories }) => categories.includes('Enterprise')), + premium: (apps: App[]) => apps.filter(({ categories }) => categories.includes('Enterprise')), requested: (apps: App[]) => apps.filter(({ appRequestStats, installed }) => Boolean(appRequestStats) && !installed), }; diff --git a/apps/meteor/client/views/marketplace/sidebarItems.tsx b/apps/meteor/client/views/marketplace/sidebarItems.tsx index bafcc4e62c58..f829cccf3238 100644 --- a/apps/meteor/client/views/marketplace/sidebarItems.tsx +++ b/apps/meteor/client/views/marketplace/sidebarItems.tsx @@ -17,9 +17,9 @@ export const { permissionGranted: (): boolean => hasAtLeastOnePermission(['access-marketplace', 'manage-apps']), }, { - href: '/marketplace/enterprise', + href: '/marketplace/premium', icon: 'lightning', - i18nLabel: 'Enterprise', + i18nLabel: 'Premium', permissionGranted: (): boolean => hasAtLeastOnePermission(['access-marketplace', 'manage-apps']), }, { diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index b1ac03bd7df7..d4b28724b6e2 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -531,6 +531,7 @@ "Apps_context_installed": "Installed", "Apps_context_requested": "Requested", "Apps_context_private": "Private Apps", + "Apps_context_premium": "Premium", "Apps_Count_Enabled": "{{count}} app enabled", "Apps_Count_Enabled_plural": "{{count}} apps enabled", "Private_Apps_Count_Enabled": "{{count}} private app enabled", @@ -4551,6 +4552,7 @@ "Search_Installed_Apps": "Search installed apps", "Search_Private_apps": "Search private apps", "Search_Requested_Apps": "Search requested apps", + "Search_Premium_Apps": "Search Premium apps", "Search_by_file_name": "Search by file name", "Search_by_username": "Search by username", "Search_by_category": "Search by category", @@ -5946,9 +5948,9 @@ "Theme_light_description": "More accessible for individuals with visual impairments and a good choice for well-lit environments.", "Theme_dark": "Dark", "Theme_dark_description": "Reduce eye strain and fatigue in low-light conditions by minimizing the amount of light emitted by the screen.", - "Enable_of_limit_apps_currently_enabled": "**{{enabled}} of {{limit}} {{context}} apps currently enabled.** \n \nWorkspaces on Community Edition can have up to {{limit}} {{context}} apps enabled. \n \n**{{appName}} will be disabled by default.** Disable another {{context}} app or upgrade to Enterprise to enable this app.", - "Enable_of_limit_apps_currently_enabled_exceeded": "**{{enabled}} of {{limit}} {{context}} apps currently enabled.** \n \nCommunity edition app limit has been exceeded. \n \nWorkspaces on Community Edition can have up to {{limit}} {{context}} apps enabled. \n \n**{{appName}} will be disabled by default.** You will need to disable at least {{exceed}} other {{context}} apps or upgrade to Enterprise to enable this app.", - "Workspaces_on_Community_edition_install_app": "Workspaces on Community Edition can have up to {{limit}} {{context}} apps enabled. Upgrade to Enterprise to enable unlimited apps.", + "Enable_of_limit_apps_currently_enabled": "**{{enabled}} of {{limit}} {{context}} apps currently enabled.** \n \nWorkspaces on Community Edition can have up to {{limit}} {{context}} apps enabled. \n \n**{{appName}} will be disabled by default.** Disable another {{context}} app or upgrade to Premium to enable this app.", + "Enable_of_limit_apps_currently_enabled_exceeded": "**{{enabled}} of {{limit}} {{context}} apps currently enabled.** \n \nCommunity edition app limit has been exceeded. \n \nWorkspaces on Community Edition can have up to {{limit}} {{context}} apps enabled. \n \n**{{appName}} will be disabled by default.** You will need to disable at least {{exceed}} other {{context}} apps or upgrade to Premium to enable this app.", + "Workspaces_on_Community_edition_install_app": "Workspaces on Community Edition can have up to {{limit}} {{context}} apps enabled. Upgrade to Premium to enable unlimited apps.", "Apps_Currently_Enabled": "{{enabled}} of {{limit}} {{context}} apps currently enabled.", "Disable_another_app": "Disable another app or upgrade to Enterprise to enable this app.", "Upload_anyway": "Upload anyway", @@ -5971,8 +5973,8 @@ "Create_a_password": "Create a password", "Create_an_account": "Create an account", "Get_all_apps": "Get all the apps your team needs", - "Workspaces_on_community_edition_trial_on": "Workspaces on Community Edition can have up to 5 marketplace apps and 3 private apps enabled. Start a free Enterprise trial to remove these limits today!", - "Workspaces_on_community_edition_trial_off": "Workspaces on Community Edition can have up to 5 marketplace apps and 3 private apps enabled. Upgrade to Enterprise to remove limits and supercharge your workspace.", + "Workspaces_on_community_edition_trial_on": "Workspaces on Community Edition can have up to 5 marketplace apps and 3 private apps enabled. Start a free Premium trial to remove these limits today!", + "Workspaces_on_community_edition_trial_off": "Workspaces on Community Edition can have up to 5 marketplace apps and 3 private apps enabled. Upgrade to Premium to remove limits and supercharge your workspace.", "No_private_apps_installed": "No private apps installed", "Private_apps_are_side-loaded": "Private apps are side-loaded and are not available on the Marketplace.", "Chat_transcript": "Chat transcript", @@ -6074,5 +6076,7 @@ "All_visible": "All visible", "Filter_by_room": "Filter by room type", "Filter_by_visibility": "Filter by visibility", - "Theme_Appearence": "Theme Appearence" + "Theme_Appearence": "Theme Appearence", + "Premium": "Premium", + "Premium_capability": "Premium capability" }