diff --git a/.changeset/rotten-foxes-marry.md b/.changeset/rotten-foxes-marry.md new file mode 100644 index 0000000000000..47e839259e579 --- /dev/null +++ b/.changeset/rotten-foxes-marry.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/i18n': patch +'@rocket.chat/meteor': patch +--- + +Fixes email notifications to display all files when a message contains multiple attachments diff --git a/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts b/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts index dd67e3489f5da..fd503b31644ce 100644 --- a/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts +++ b/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts @@ -6,6 +6,7 @@ import type { AtLeast, FilesAndAttachments, IMessage, + FileProp, } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Rooms, Uploads, Users } from '@rocket.chat/models'; @@ -42,13 +43,14 @@ export const parseFileIntoMessageAttachments = async ( const attachments: MessageAttachment[] = []; - const files = [ + const files: FileProp[] = [ { _id: file._id, name: file.name || '', type: file.type || 'file', size: file.size || 0, format: file.identify?.format || '', + typeGroup: file.typeGroup, }, ]; @@ -96,6 +98,7 @@ export const parseFileIntoMessageAttachments = async ( type: thumbnail.type || 'file', size: thumbnail.size || 0, format: thumbnail.identify?.format || '', + typeGroup: thumbnail.typeGroup || '', }); } } catch (e) { diff --git a/apps/meteor/app/lib/server/functions/notifications/email.js b/apps/meteor/app/lib/server/functions/notifications/email.js index fa99f7eeb2048..07cc5c949121b 100644 --- a/apps/meteor/app/lib/server/functions/notifications/email.js +++ b/apps/meteor/app/lib/server/functions/notifications/email.js @@ -29,23 +29,43 @@ export async function getEmailContent({ message, user, room }) { const roomDirectives = roomCoordinator.getRoomDirectives(room.t); - const header = i18n.t(!roomDirectives.isGroupChat(room) ? 'User_sent_a_message_to_you' : 'User_sent_a_message_on_channel', { - username: userName, - channel: roomName, - lng, - }); + const files = (message.files || [message.file]).filter((file) => file && file.typeGroup !== 'thumb'); + const hasFiles = files.length > 0; + const hasText = typeof message.msg === 'string' && message.msg.trim() !== ''; + const isGroupChat = roomDirectives.isGroupChat(room); + + let header; + if (hasFiles && !hasText) { + // file-only message + const isMultipleFiles = files.length > 1; + let headerKey; + if (isGroupChat) { + headerKey = isMultipleFiles ? 'User_uploaded_files_on_channel' : 'User_uploaded_a_file_on_channel'; + } else { + headerKey = isMultipleFiles ? 'User_uploaded_files_to_you' : 'User_uploaded_a_file_to_you'; + } + header = i18n.t(headerKey, { username: userName, channel: roomName, count: files.length, lng }); + } else { + // text message (with or without files) + header = i18n.t(!isGroupChat ? 'User_sent_a_message_to_you' : 'User_sent_a_message_on_channel', { + username: userName, + channel: roomName, + lng, + }); + } - if (message.t === 'e2e' && !message.file) { + if (message.t === 'e2e' && !message.file && !message.files?.length) { return settings.get('Email_notification_show_message') ? i18n.t('Encrypted_message_preview_unavailable', { lng }) : header; } - if (message.msg !== '') { - if (!settings.get('Email_notification_show_message')) { - return header; - } + if (!settings.get('Email_notification_show_message')) { + return header; + } - let messageContent = escapeHTML(message.msg); + const contentParts = []; + if (hasText) { + let messageContent = escapeHTML(message.msg); message = await callbacks.run('renderMessage', message); if (message.tokens && message.tokens.length > 0) { message.tokens.forEach((token) => { @@ -53,31 +73,23 @@ export async function getEmailContent({ message, user, room }) { messageContent = messageContent.replace(token.token, token.text); }); } - return `${header}:

${messageContent.replace(/\n/gm, '
')}`; + contentParts.push(messageContent.replace(/\n/gm, '
')); } - if (message.file) { - const fileHeader = i18n.t(!roomDirectives.isGroupChat(room) ? 'User_uploaded_a_file_to_you' : 'User_uploaded_a_file_on_channel', { - username: userName, - channel: roomName, - lng, + if (hasFiles) { + const attachments = message.attachments || []; + const fileParts = files.map((file, index) => { + let part = escapeHTML(file.name); + if (attachments[index]?.description) { + part += `

${escapeHTML(attachments[index].description)}`; + } + return part; }); - - if (!settings.get('Email_notification_show_message')) { - return fileHeader; - } - - let content = `${escapeHTML(message.file.name)}`; - - if (message.attachments && message.attachments.length === 1 && message.attachments[0].description !== '') { - content += `

${escapeHTML(message.attachments[0].description)}`; - } - - return `${fileHeader}:

${content}`; + contentParts.push(fileParts.join('

')); } - if (!settings.get('Email_notification_show_message')) { - return header; + if (contentParts.length > 0) { + return `${header}:

${contentParts.join('

')}`; } if (Array.isArray(message.attachments) && message.attachments.length > 0) { diff --git a/apps/meteor/client/lib/links.ts b/apps/meteor/client/lib/links.ts index 2a1d06ad9da88..4889f8a8a1264 100644 --- a/apps/meteor/client/lib/links.ts +++ b/apps/meteor/client/lib/links.ts @@ -33,6 +33,7 @@ export const links = { updateProduct: `${GO_ROCKET_CHAT_PREFIX}/i/update-product`, abacDocs: `${GO_ROCKET_CHAT_PREFIX}/i/abac`, abacLDAPDocs: `${GO_ROCKET_CHAT_PREFIX}/i/abac-ldap`, + logsDocs: `${GO_ROCKET_CHAT_PREFIX}/i/logs-docs`, }, /** @deprecated use `go.rocket.chat` links */ desktopAppDownload: 'https://rocket.chat/download', diff --git a/apps/meteor/client/views/admin/viewLogs/AnalyticsReports.tsx b/apps/meteor/client/views/admin/viewLogs/AnalyticsReports.tsx index 8891f9170a896..80502fa58e502 100644 --- a/apps/meteor/client/views/admin/viewLogs/AnalyticsReports.tsx +++ b/apps/meteor/client/views/admin/viewLogs/AnalyticsReports.tsx @@ -1,6 +1,8 @@ -import { Box, Icon, Skeleton, Scrollable } from '@rocket.chat/fuselage'; +import { Box, Icon, Skeleton, Scrollable, Callout } from '@rocket.chat/fuselage'; import { useTranslation } from 'react-i18next'; +import MarkdownText from '../../../components/MarkdownText'; +import { links } from '../../../lib/links'; import { useStatistics } from '../../hooks/useStatistics'; const AnalyticsReports = () => { @@ -10,6 +12,14 @@ const AnalyticsReports = () => { return ( + +

+ +

+
diff --git a/packages/core-typings/src/IMessage/MessageAttachment/Files/FileProp.ts b/packages/core-typings/src/IMessage/MessageAttachment/Files/FileProp.ts index b13ab3fd9931a..628391ff5089d 100644 --- a/packages/core-typings/src/IMessage/MessageAttachment/Files/FileProp.ts +++ b/packages/core-typings/src/IMessage/MessageAttachment/Files/FileProp.ts @@ -4,4 +4,7 @@ export type FileProp = { type: string; format: string; size: number; + // used to filter out thumbnails in email notifications + // may not exist in old messages + typeGroup?: string; }; diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 02ae126a8d8cc..5db49d7100676 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -4834,6 +4834,8 @@ "Server_File_Path": "Server File Path", "Server_Folder_Path": "Server Folder Path", "Server_Info": "Server Info", + "Server_logs_access_has_changed_callout_title": "Server logs access has changed in Rocket.Chat 8.0", + "Server_logs_access_has_changed_callout_description": "The logs viewer was removed. We recommend configuring an observability stack and using the supported log access methods described in the [logging guide]({{docsUrl}}).", "Server_Type": "Server Type", "Server_already_added": "Server already added", "Server_doesnt_exist": "Server doesn't exist", @@ -5641,6 +5643,8 @@ "User_updated_successfully": "User updated successfully", "User_uploaded_a_file_on_channel": "{{username}} uploaded a file on {{channel}}", "User_uploaded_a_file_to_you": "{{username}} sent you a file", + "User_uploaded_files_on_channel": "{{username}} uploaded {{count}} files on {{channel}}", + "User_uploaded_files_to_you": "{{username}} sent you {{count}} files", "User_uploaded_file": "Uploaded a file", "User_uploaded_image": "Uploaded an image", "Username": "Username",