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",