diff --git a/.changeset/cold-vans-suffer.md b/.changeset/cold-vans-suffer.md
new file mode 100644
index 0000000000000..3d195b9a22b21
--- /dev/null
+++ b/.changeset/cold-vans-suffer.md
@@ -0,0 +1,6 @@
+---
+'@rocket.chat/core-typings': patch
+'@rocket.chat/meteor': patch
+---
+
+Added per-channel link preview setting
diff --git a/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.ts b/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.ts
index 56dbef2d1cdb1..1f8c5bc312078 100644
--- a/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.ts
+++ b/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.ts
@@ -44,6 +44,7 @@ type RoomSettings = {
retentionIgnoreThreads: boolean;
retentionOverrideGlobal: boolean;
encrypted: boolean;
+ linksEmbed: boolean;
favorite: {
favorite: boolean;
defaultValue: boolean;
@@ -265,6 +266,10 @@ const settingSavers: RoomSettingsSavers = {
await saveRoomTopic(rid, value, user);
}
},
+ async linksEmbed({ value, rid }) {
+ // This saves the value directly to the room document in MongoDB
+ await Rooms.updateOne({ _id: rid }, { $set: { linksEmbed: value } });
+ },
async roomAnnouncement({ value, room, rid, user }) {
if (!value && !room.announcement) {
return;
@@ -386,6 +391,8 @@ const fields: (keyof RoomSettings)[] = [
'retentionIgnoreThreads',
'retentionOverrideGlobal',
'encrypted',
+
+ 'linksEmbed',
'favorite',
];
diff --git a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx
index 59c198f669c17..ddd953d0e2257 100644
--- a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx
+++ b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx
@@ -228,6 +228,7 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
const retentionExcludePinnedField = useId();
const retentionFilesOnlyField = useId();
const retentionIgnoreThreads = useId();
+ const linksEmbedField = useId();
const showAdvancedSettings = canViewReadOnly || readOnly || canViewArchived || canViewJoinCode || canViewHideSysMes;
const showRetentionPolicy = canEditRoomRetentionPolicy && retentionPolicy?.enabled;
@@ -489,6 +490,26 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
)}
+
+
+
+ {t('Enable_Link_Previews' as any)}
+ (
+
+ )}
+ />
+
+ {t('Enable_Link_Previews_Description' as any)}
+
)}
diff --git a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/useEditRoomInitialValues.ts b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/useEditRoomInitialValues.ts
index 5aeb06a3a2114..29589ec9c28dd 100644
--- a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/useEditRoomInitialValues.ts
+++ b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/useEditRoomInitialValues.ts
@@ -28,6 +28,7 @@ export type EditRoomInfoFormData = {
showChannels: boolean;
showDiscussions: boolean;
joinCode: string;
+ linksEmbed: boolean;
systemMessages: MessageTypesValues[];
};
@@ -52,6 +53,7 @@ export const useEditRoomInitialValues = (room: IRoomWithRetentionPolicy): Partia
joinCodeRequired: !!joinCodeRequired,
systemMessages: Array.isArray(sysMes) ? sysMes : [],
hideSysMes: Array.isArray(sysMes) ? !!sysMes?.length : !!sysMes,
+ linksEmbed: room.linksEmbed !== false,
encrypted,
...(canEditRoomRetentionPolicy &&
retentionPolicy?.enabled && {
diff --git a/apps/meteor/server/services/messages/hooks/AfterSaveOEmbed.ts b/apps/meteor/server/services/messages/hooks/AfterSaveOEmbed.ts
index 3623b3ddd90ae..9cc6c9a99cc66 100644
--- a/apps/meteor/server/services/messages/hooks/AfterSaveOEmbed.ts
+++ b/apps/meteor/server/services/messages/hooks/AfterSaveOEmbed.ts
@@ -8,7 +8,7 @@ import type {
} from '@rocket.chat/core-typings';
import { isOEmbedUrlWithMetadata } from '@rocket.chat/core-typings';
import { Logger } from '@rocket.chat/logger';
-import { OEmbedCache, Messages } from '@rocket.chat/models';
+import { OEmbedCache, Messages, Rooms } from '@rocket.chat/models';
import { serverFetch as fetch } from '@rocket.chat/server-fetch';
import he from 'he';
import iconv from 'iconv-lite';
@@ -327,19 +327,21 @@ const getRelevantMetaTags = function (metaObj: OEmbedMeta): Record
oembedHtml?.replace('iframe', 'iframe style="max-width: 100%;width:400px;height:225px"');
+
+
const rocketUrlParser = async function (message: IMessage): Promise {
log.debug({ msg: 'Parsing message URLs' });
-
if (!settings.get('API_Embed')) {
return message;
}
-
if (!Array.isArray(message.urls)) {
return message;
}
-
+ const room = await Rooms.findOneById(message.rid, { projection: { linksEmbed: 1 } });
+ if (room?.linksEmbed === false) {
+ return message;
+ }
log.debug({ msg: 'URLs found in message', count: message.urls.length });
-
if (
(message.attachments && message.attachments.length > 0) ||
message.urls.filter((item) => !item.url.includes(settings.get('Site_Url'))).length > MAX_EXTERNAL_URL_PREVIEWS
@@ -347,24 +349,19 @@ const rocketUrlParser = async function (message: IMessage): Promise {
log.debug({ msg: 'All URLs ignored for OEmbed' });
return message;
}
-
let changed = false;
for await (const item of message.urls) {
if (item.ignoreParse === true) {
log.debug({ msg: 'URL ignored for OEmbed', url: item.url });
continue;
}
-
const { urlPreview, foundMeta } = await parseUrl(item.url);
-
Object.assign(item, foundMeta ? urlPreview : {});
changed = changed || foundMeta;
}
-
if (changed === true) {
await Messages.setUrlsById(message._id, message.urls);
}
-
return message;
};
diff --git a/packages/core-typings/src/IRoom.ts b/packages/core-typings/src/IRoom.ts
index fb243a8f92e52..481eca77932d1 100644
--- a/packages/core-typings/src/IRoom.ts
+++ b/packages/core-typings/src/IRoom.ts
@@ -10,6 +10,10 @@ import type { RoomType } from './RoomType';
import type { Branded } from './utils';
export interface IRoom extends IRocketChatRecord {
+ /**
+ * Enable or disable link previews for this room. If undefined, falls back to global setting.
+ */
+ linksEmbed?: boolean;
t: RoomType;
name?: string;
fname?: string;
@@ -361,6 +365,10 @@ export type RoomAdminFieldsType =
| 'abacAttributes';
export interface IRoomWithRetentionPolicy extends IRoom {
+ /**
+ * Enable or disable link previews for this room. If undefined, falls back to global setting.
+ */
+ linksEmbed?: boolean;
retention: {
enabled?: boolean;
maxAge: number;
diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json
index f5e7375bddce8..befdc432bd646 100644
--- a/packages/i18n/src/locales/en.i18n.json
+++ b/packages/i18n/src/locales/en.i18n.json
@@ -2062,6 +2062,7 @@
"Enterprise_capabilities": "Enterprise capabilities",
"Enterprise_capability": "Enterprise capability",
"Entertainment": "Entertainment",
+ "Enable_Link_Previews": "Enable Link Previews",
"Error": "Error",
"Error_404": "Error:404",
"Error_RocketChat_requires_oplog_tailing_when_running_in_multiple_instances": "Error: Rocket.Chat requires oplog tailing when running in multiple instances",