diff --git a/packages/rocketchat-lib/lib/getUserNotificationPreference.js b/packages/rocketchat-lib/lib/getUserNotificationPreference.js
new file mode 100644
index 000000000000..27ae25337cc0
--- /dev/null
+++ b/packages/rocketchat-lib/lib/getUserNotificationPreference.js
@@ -0,0 +1,28 @@
+RocketChat.getUserNotificationPreference = function _getUserNotificationPreference(user, pref) {
+ if (typeof user === 'string') {
+ user = RocketChat.models.Users.findOneById(user);
+ }
+
+ let preferenceKey;
+ switch (pref) {
+ case 'desktop': preferenceKey = 'desktopNotifications'; break;
+ case 'mobile': preferenceKey = 'mobileNotifications'; break;
+ case 'email': preferenceKey = 'emailNotificationMode'; break;
+ }
+
+ if (user && user.settings && user.settings.preferences && user.settings.preferences[preferenceKey] !== 'default') {
+ return {
+ value: user.settings.preferences[preferenceKey],
+ origin: 'user'
+ };
+ }
+ const serverValue = RocketChat.settings.get(`Accounts_Default_User_Preferences_${ preferenceKey }`);
+ if (serverValue) {
+ return {
+ value: serverValue,
+ origin: 'server'
+ };
+ }
+
+ return null;
+};
diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js
index 9f3ba06887f2..cfc092292f67 100644
--- a/packages/rocketchat-lib/package.js
+++ b/packages/rocketchat-lib/package.js
@@ -68,6 +68,7 @@ Package.onUse(function(api) {
api.addFiles('lib/MessageTypes.js');
api.addFiles('lib/templateVarHandler.js');
+ api.addFiles('lib/getUserNotificationPreference.js');
api.addFiles('lib/getUserPreference.js');
api.addFiles('server/lib/bugsnag.js', 'server');
@@ -112,7 +113,6 @@ Package.onUse(function(api) {
api.addFiles('server/lib/notifyUsersOnMessage.js', 'server');
api.addFiles('server/lib/processDirectEmail.js', 'server');
api.addFiles('server/lib/roomTypes.js', 'server');
- api.addFiles('server/lib/sendEmailOnMessage.js', 'server');
api.addFiles('server/lib/sendNotificationsOnMessage.js', 'server');
api.addFiles('server/lib/validateEmailDomain.js', 'server');
diff --git a/packages/rocketchat-lib/server/functions/createRoom.js b/packages/rocketchat-lib/server/functions/createRoom.js
index 4fa617950ee8..d028294354b0 100644
--- a/packages/rocketchat-lib/server/functions/createRoom.js
+++ b/packages/rocketchat-lib/server/functions/createRoom.js
@@ -64,7 +64,7 @@ RocketChat.createRoom = function(type, name, owner, members, readOnly, extraData
room = RocketChat.models.Rooms.createWithFullRoomData(room);
for (const username of members) {
- const member = RocketChat.models.Users.findOneByUsername(username, { fields: { username: 1 }});
+ const member = RocketChat.models.Users.findOneByUsername(username, { fields: { username: 1, 'settings.preferences': 1 }});
const isTheOwner = username === owner.username;
if (!member) {
continue;
diff --git a/packages/rocketchat-lib/server/functions/notifications/audio.js b/packages/rocketchat-lib/server/functions/notifications/audio.js
new file mode 100644
index 000000000000..1c45b18f336a
--- /dev/null
+++ b/packages/rocketchat-lib/server/functions/notifications/audio.js
@@ -0,0 +1,35 @@
+export function shouldNotifyAudio({
+ disableAllMessageNotifications,
+ status,
+ audioNotifications,
+ hasMentionToAll,
+ hasMentionToHere,
+ isHighlighted,
+ hasMentionToUser
+}) {
+ if (disableAllMessageNotifications && audioNotifications == null) {
+ return false;
+ }
+
+ if (status === 'busy' || audioNotifications === 'nothing') {
+ return false;
+ }
+
+ if (!audioNotifications && RocketChat.settings.get('Accounts_Default_User_Preferences_audioNotifications') === 'all') {
+ return true;
+ }
+
+ return (!disableAllMessageNotifications && (hasMentionToAll || hasMentionToHere)) || isHighlighted || audioNotifications === 'all' || hasMentionToUser;
+}
+
+export function notifyAudioUser(userId, message, room) {
+ RocketChat.Notifications.notifyUser(userId, 'audioNotification', {
+ payload: {
+ _id: message._id,
+ rid: message.rid,
+ sender: message.u,
+ type: room.t,
+ name: room.name
+ }
+ });
+}
diff --git a/packages/rocketchat-lib/server/functions/notifications/desktop.js b/packages/rocketchat-lib/server/functions/notifications/desktop.js
new file mode 100644
index 000000000000..a520c216ea4f
--- /dev/null
+++ b/packages/rocketchat-lib/server/functions/notifications/desktop.js
@@ -0,0 +1,94 @@
+import { parseMessageText } from './index';
+
+/**
+ * Replaces @username with full name
+ *
+ * @param {string} message The message to replace
+ * @param {object[]} mentions Array of mentions used to make replacements
+ *
+ * @returns {string}
+ */
+function replaceMentionedUsernamesWithFullNames(message, mentions) {
+ if (!mentions || !mentions.length) {
+ return message;
+ }
+ mentions.forEach((mention) => {
+ const user = RocketChat.models.Users.findOneById(mention._id);
+ if (user && user.name) {
+ message = message.replace(`@${ mention.username }`, user.name);
+ }
+ });
+ return message;
+}
+
+/**
+ * Send notification to user
+ *
+ * @param {string} userId The user to notify
+ * @param {object} user The sender
+ * @param {object} room The room send from
+ * @param {number} duration Duration of notification
+ */
+export function notifyDesktopUser(userId, user, message, room, duration) {
+
+ const UI_Use_Real_Name = RocketChat.settings.get('UI_Use_Real_Name') === true;
+ message.msg = parseMessageText(message, userId);
+
+ if (UI_Use_Real_Name) {
+ message.msg = replaceMentionedUsernamesWithFullNames(message.msg, message.mentions);
+ }
+
+ let title = '';
+ let text = '';
+ if (room.t === 'd') {
+ title = UI_Use_Real_Name ? user.name : `@${ user.username }`;
+ text = message.msg;
+ } else if (room.name) {
+ title = `#${ room.name }`;
+ text = `${ UI_Use_Real_Name ? user.name : user.username }: ${ message.msg }`;
+ } else {
+ return;
+ }
+
+ RocketChat.Notifications.notifyUser(userId, 'notification', {
+ title,
+ text,
+ duration,
+ payload: {
+ _id: message._id,
+ rid: message.rid,
+ sender: message.u,
+ type: room.t,
+ name: room.name
+ }
+ });
+}
+
+export function shouldNotifyDesktop({
+ disableAllMessageNotifications,
+ status,
+ desktopNotifications,
+ hasMentionToAll,
+ hasMentionToHere,
+ isHighlighted,
+ hasMentionToUser
+}) {
+ if (disableAllMessageNotifications && desktopNotifications == null) {
+ return false;
+ }
+
+ if (status === 'busy' || desktopNotifications === 'nothing') {
+ return false;
+ }
+
+ if (!desktopNotifications) {
+ if (RocketChat.settings.get('Accounts_Default_User_Preferences_desktopNotifications') === 'all') {
+ return true;
+ }
+ if (RocketChat.settings.get('Accounts_Default_User_Preferences_desktopNotifications') === 'nothing') {
+ return false;
+ }
+ }
+
+ return (!disableAllMessageNotifications && (hasMentionToAll || hasMentionToHere)) || isHighlighted || desktopNotifications === 'all' || hasMentionToUser;
+}
diff --git a/packages/rocketchat-lib/server/functions/notifications/email.js b/packages/rocketchat-lib/server/functions/notifications/email.js
new file mode 100644
index 000000000000..e0a887855cf5
--- /dev/null
+++ b/packages/rocketchat-lib/server/functions/notifications/email.js
@@ -0,0 +1,176 @@
+import s from 'underscore.string';
+
+let contentHeader;
+RocketChat.settings.get('Email_Header', (key, value) => {
+ contentHeader = RocketChat.placeholders.replace(value || '');
+});
+
+let contentFooter;
+RocketChat.settings.get('Email_Footer', (key, value) => {
+ contentFooter = RocketChat.placeholders.replace(value || '');
+});
+
+const divisorMessage = '
';
+
+function getEmailContent({ message, user, room }) {
+ const lng = user && user.language || RocketChat.settings.get('language') || 'en';
+
+ const roomName = s.escapeHTML(`#${ RocketChat.roomTypes.getRoomName(room.t, room) }`);
+ const userName = s.escapeHTML(RocketChat.settings.get('UI_Use_Real_Name') ? message.u.name || message.u.username : message.u.username);
+
+ const header = TAPi18n.__(room.t === 'd' ? 'User_sent_a_message_to_you' : 'User_sent_a_message_on_channel', {
+ username: userName,
+ channel: roomName,
+ lng
+ });
+
+ if (message.msg !== '') {
+ let messageContent = s.escapeHTML(message.msg);
+ message = RocketChat.callbacks.run('renderMessage', message);
+ if (message.tokens && message.tokens.length > 0) {
+ message.tokens.forEach((token) => {
+ token.text = token.text.replace(/([^\$])(\$[^\$])/gm, '$1$$$2');
+ messageContent = messageContent.replace(token.token, token.text);
+ });
+ }
+ return `${ header }
${ messageContent.replace(/\n/gm, '
') }`;
+ }
+
+ if (message.file) {
+ const fileHeader = TAPi18n.__(room.t === 'd' ? 'User_uploaded_a_file_to_you' : 'User_uploaded_a_file_on_channel', {
+ username: userName,
+ channel: roomName,
+ lng
+ });
+
+ let content = `${ TAPi18n.__('Attachment_File_Uploaded') }: ${ s.escapeHTML(message.file.name) }`;
+
+ if (message.attachments && message.attachments.length === 1 && message.attachments[0].description !== '') {
+ content += `
${ s.escapeHTML(message.attachments[0].description) }`;
+ }
+
+ return `${ fileHeader }
${ content }`;
+ }
+
+ if (message.attachments.length > 0) {
+ const [ attachment ] = message.attachments;
+
+ let content = '';
+
+ if (attachment.title) {
+ content += `${ s.escapeHTML(attachment.title) }
`;
+ }
+ if (attachment.text) {
+ content += `${ s.escapeHTML(attachment.text) }
`;
+ }
+
+ return `${ header }
${ content }`;
+ }
+
+ return header;
+}
+
+function getMessageLink(room, sub) {
+ const roomPath = RocketChat.roomTypes.getRouteLink(room.t, sub);
+ const path = Meteor.absoluteUrl(roomPath ? roomPath.replace(/^\//, '') : '');
+ const style = [
+ 'color: #fff;',
+ 'padding: 9px 12px;',
+ 'border-radius: 4px;',
+ 'background-color: #04436a;',
+ 'text-decoration: none;'
+ ].join(' ');
+ const message = TAPi18n.__('Offline_Link_Message');
+ return `${ message }`;
+}
+
+export function sendEmail({ message, user, subscription, room, emailAddress, toAll }) {
+ let emailSubject;
+ const username = RocketChat.settings.get('UI_Use_Real_Name') ? message.u.name : message.u.username;
+
+ if (room.t === 'd') {
+ emailSubject = RocketChat.placeholders.replace(RocketChat.settings.get('Offline_DM_Email'), {
+ user: username,
+ room: RocketChat.roomTypes.getRoomName(room.t, room)
+ });
+ } else if (toAll) {
+ emailSubject = RocketChat.placeholders.replace(RocketChat.settings.get('Offline_Mention_All_Email'), {
+ user: username,
+ room: RocketChat.roomTypes.getRoomName(room.t, room)
+ });
+ } else {
+ emailSubject = RocketChat.placeholders.replace(RocketChat.settings.get('Offline_Mention_Email'), {
+ user: username,
+ room: RocketChat.roomTypes.getRoomName(room.t, room)
+ });
+ }
+ const content = getEmailContent({
+ message,
+ user,
+ room
+ });
+
+ const link = getMessageLink(room, subscription);
+
+ if (RocketChat.settings.get('Direct_Reply_Enable')) {
+ contentFooter = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Footer_Direct_Reply') || '');
+ }
+
+ const email = {
+ to: emailAddress,
+ subject: emailSubject,
+ html: contentHeader + content + divisorMessage + link + contentFooter
+ };
+
+ // using user full-name/channel name in from address
+ if (room.t === 'd') {
+ email.from = `${ String(message.u.name).replace(/@/g, '%40').replace(/[<>,]/g, '') } <${ RocketChat.settings.get('From_Email') }>`;
+ } else {
+ email.from = `${ String(room.name).replace(/@/g, '%40').replace(/[<>,]/g, '') } <${ RocketChat.settings.get('From_Email') }>`;
+ }
+ // If direct reply enabled, email content with headers
+ if (RocketChat.settings.get('Direct_Reply_Enable')) {
+ email.headers = {
+ // Reply-To header with format "username+messageId@domain"
+ 'Reply-To': `${ RocketChat.settings.get('Direct_Reply_Username').split('@')[0].split(RocketChat.settings.get('Direct_Reply_Separator'))[0] }${ RocketChat.settings.get('Direct_Reply_Separator') }${ message._id }@${ RocketChat.settings.get('Direct_Reply_Username').split('@')[1] }`
+ };
+ }
+
+ Meteor.defer(() => {
+ Email.send(email);
+ });
+}
+
+export function shouldNotifyEmail({
+ disableAllMessageNotifications,
+ statusConnection,
+ emailNotifications,
+ isHighlighted,
+ hasMentionToUser,
+ hasMentionToAll
+}) {
+
+ // use connected (don't need to send him an email)
+ if (statusConnection === 'online') {
+ return false;
+ }
+
+ // user/room preference to nothing
+ if (emailNotifications === 'nothing') {
+ return false;
+ }
+
+ // no user or room preference
+ if (emailNotifications == null) {
+ if (disableAllMessageNotifications) {
+ return false;
+ }
+
+ // default server preference is disabled
+ if (RocketChat.settings.get('Accounts_Default_User_Preferences_emailNotificationMode') === 'disabled') {
+ return false;
+ }
+ }
+
+ return isHighlighted || emailNotifications === 'all' || hasMentionToUser || (!disableAllMessageNotifications && hasMentionToAll);
+}
diff --git a/packages/rocketchat-lib/server/functions/notifications/index.js b/packages/rocketchat-lib/server/functions/notifications/index.js
new file mode 100644
index 000000000000..d9f6e74d65bb
--- /dev/null
+++ b/packages/rocketchat-lib/server/functions/notifications/index.js
@@ -0,0 +1,46 @@
+import s from 'underscore.string';
+
+/**
+* This function returns a string ready to be shown in the notification
+*
+* @param {object} message the message to be parsed
+*/
+export function parseMessageText(message, userId) {
+ const user = RocketChat.models.Users.findOneById(userId);
+ const lng = user && user.language || RocketChat.settings.get('language') || 'en';
+
+ if (!message.msg && message.attachments && message.attachments[0]) {
+ message.msg = message.attachments[0].image_type ? TAPi18n.__('User_uploaded_image', {lng}) : TAPi18n.__('User_uploaded_file', {lng});
+ }
+ message.msg = RocketChat.callbacks.run('beforeNotifyUser', message.msg);
+
+ return message.msg;
+}
+
+/**
+ * Checks if a message contains a user highlight
+ *
+ * @param {string} message
+ * @param {array|undefined} highlights
+ *
+ * @returns {boolean}
+ */
+export function messageContainsHighlight(message, highlights) {
+ if (! highlights || highlights.length === 0) { return false; }
+
+ return highlights.some(function(highlight) {
+ const regexp = new RegExp(s.escapeRegExp(highlight), 'i');
+ return regexp.test(message.msg);
+ });
+}
+
+export function callJoinRoom(user, rid) {
+ return new Promise((resolve, reject) => {
+ Meteor.runAsUser(user._id, () => Meteor.call('joinRoom', rid, (error, result) => {
+ if (error) {
+ return reject(error);
+ }
+ return resolve(result);
+ }));
+ });
+}
diff --git a/packages/rocketchat-lib/server/functions/notifications/mobile.js b/packages/rocketchat-lib/server/functions/notifications/mobile.js
new file mode 100644
index 000000000000..820839b3fad9
--- /dev/null
+++ b/packages/rocketchat-lib/server/functions/notifications/mobile.js
@@ -0,0 +1,74 @@
+import { parseMessageText } from './index';
+
+const CATEGORY_MESSAGE = 'MESSAGE';
+const CATEGORY_MESSAGE_NOREPLY = 'MESSAGE_NOREPLY';
+
+let alwaysNotifyMobileBoolean;
+RocketChat.settings.get('Notifications_Always_Notify_Mobile', (key, value) => {
+ alwaysNotifyMobileBoolean = value;
+});
+
+// function getBadgeCount(userId) {
+// const subscriptions = RocketChat.models.Subscriptions.findUnreadByUserId(userId).fetch();
+
+// return subscriptions.reduce((unread, sub) => {
+// return sub.unread + unread;
+// }, 0);
+// }
+
+function canSendMessageToRoom(room, username) {
+ return !((room.muted || []).includes(username));
+}
+
+export function sendSinglePush({ room, message, userId, receiverUsername, senderUsername }) {
+ RocketChat.PushNotification.send({
+ roomId: message.rid,
+ payload: {
+ host: Meteor.absoluteUrl(),
+ rid: message.rid,
+ sender: message.u,
+ type: room.t,
+ name: room.name
+ },
+ roomName: RocketChat.settings.get('Push_show_username_room') ? `#${ RocketChat.roomTypes.getRoomName(room.t, room) }` : '',
+ username: RocketChat.settings.get('Push_show_username_room') ? senderUsername : '',
+ message: RocketChat.settings.get('Push_show_message') ? parseMessageText(message, userId) : ' ',
+ // badge: getBadgeCount(userIdToNotify),
+ usersTo: {
+ userId
+ },
+ category: canSendMessageToRoom(room, receiverUsername) ? CATEGORY_MESSAGE : CATEGORY_MESSAGE_NOREPLY
+ });
+}
+
+export function shouldNotifyMobile({
+ disableAllMessageNotifications,
+ mobilePushNotifications,
+ hasMentionToAll,
+ isHighlighted,
+ hasMentionToUser,
+ statusConnection
+}) {
+ if (disableAllMessageNotifications && mobilePushNotifications == null) {
+ return false;
+ }
+
+ if (mobilePushNotifications === 'nothing') {
+ return false;
+ }
+
+ if (!alwaysNotifyMobileBoolean && statusConnection === 'online') {
+ return false;
+ }
+
+ if (!mobilePushNotifications) {
+ if (RocketChat.settings.get('Accounts_Default_User_Preferences_mobileNotifications') === 'all') {
+ return true;
+ }
+ if (RocketChat.settings.get('Accounts_Default_User_Preferences_mobileNotifications') === 'nothing') {
+ return false;
+ }
+ }
+
+ return (!disableAllMessageNotifications && hasMentionToAll) || isHighlighted || mobilePushNotifications === 'all' || hasMentionToUser;
+}
diff --git a/packages/rocketchat-lib/server/lib/notifyUsersOnMessage.js b/packages/rocketchat-lib/server/lib/notifyUsersOnMessage.js
index f673c56ec103..45502db7b461 100644
--- a/packages/rocketchat-lib/server/lib/notifyUsersOnMessage.js
+++ b/packages/rocketchat-lib/server/lib/notifyUsersOnMessage.js
@@ -2,7 +2,25 @@ import _ from 'underscore';
import s from 'underscore.string';
import moment from 'moment';
-RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
+/**
+ * Chechs if a messages contains a user highlight
+ *
+ * @param {string} message
+ * @param {array|undefined} highlights
+ *
+ * @returns {boolean}
+ */
+
+export function messageContainsHighlight(message, highlights) {
+ if (! highlights || highlights.length === 0) { return false; }
+
+ return highlights.some(function(highlight) {
+ const regexp = new RegExp(s.escapeRegExp(highlight), 'i');
+ return regexp.test(message.msg);
+ });
+}
+
+function notifyUsersOnMessage(message, room) {
// skips this callback if the message was edited and increments it if the edit was way in the past (aka imported)
if (message.editedAt && Math.abs(moment(message.editedAt).diff()) > 60000) {
//TODO: Review as I am not sure how else to get around this as the incrementing of the msgs count shouldn't be in this callback
@@ -22,29 +40,6 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
return message;
}
- /**
- * Chechs if a messages contains a user highlight
- *
- * @param {string} message
- * @param {array|undefined} highlights
- *
- * @returns {boolean}
- */
- function messageContainsHighlight(message, highlights) {
- if (! highlights || highlights.length === 0) { return false; }
-
- let has = false;
- highlights.some(function(highlight) {
- const regexp = new RegExp(s.escapeRegExp(highlight), 'i');
- if (regexp.test(message.msg)) {
- has = true;
- return true;
- }
- });
-
- return has;
- }
-
if (room != null) {
let toAll = false;
let toHere = false;
@@ -94,6 +89,7 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
incUnread = 1;
}
RocketChat.models.Subscriptions.incGroupMentionsAndUnreadForRoomIdExcludingUserId(room._id, message.u._id, 1, incUnread);
+
} else if ((mentionIds && mentionIds.length > 0) || (highlightsIds && highlightsIds.length > 0)) {
let incUnread = 0;
if (['all_messages', 'user_mentions_only', 'user_and_group_mentions_only'].includes(unreadCount)) {
@@ -111,8 +107,11 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
// Update all other subscriptions to alert their owners but witout incrementing
// the unread counter, as it is only for mentions and direct messages
+ // We now set alert and open properties in two separate update commands. This proved to be more efficient on MongoDB - because it uses a more efficient index.
RocketChat.models.Subscriptions.setAlertForRoomIdExcludingUserId(message.rid, message.u._id);
+ RocketChat.models.Subscriptions.setOpenForRoomIdExcludingUserId(message.rid, message.u._id);
return message;
+}
-}, RocketChat.callbacks.priority.LOW, 'notifyUsersOnMessage');
+RocketChat.callbacks.add('afterSaveMessage', notifyUsersOnMessage, RocketChat.callbacks.priority.LOW, 'notifyUsersOnMessage');
diff --git a/packages/rocketchat-lib/server/lib/roomTypes.js b/packages/rocketchat-lib/server/lib/roomTypes.js
index 68aec9b5ef6f..e6ee83c8bf70 100644
--- a/packages/rocketchat-lib/server/lib/roomTypes.js
+++ b/packages/rocketchat-lib/server/lib/roomTypes.js
@@ -31,6 +31,9 @@ RocketChat.roomTypes = new class roomTypesServer extends RoomTypesCommon {
return this.roomTypes[roomType] && this.roomTypes[roomType].roomFind;
}
+ getRoomName(roomType, roomData) {
+ return this.roomTypes[roomType] && this.roomTypes[roomType].roomName && this.roomTypes[roomType].roomName(roomData);
+ }
/**
* Run the publish for a room type
diff --git a/packages/rocketchat-lib/server/lib/sendEmailOnMessage.js b/packages/rocketchat-lib/server/lib/sendEmailOnMessage.js
deleted file mode 100644
index 860f2232860a..000000000000
--- a/packages/rocketchat-lib/server/lib/sendEmailOnMessage.js
+++ /dev/null
@@ -1,269 +0,0 @@
-import moment from 'moment';
-import s from 'underscore.string';
-
-function getEmailContent({ messageContent, message, user, room }) {
- const lng = user && user.language || RocketChat.settings.get('language') || 'en';
-
- const roomName = s.escapeHTML(`#${ RocketChat.settings.get('UI_Allow_room_names_with_special_chars') ? room.fname || room.name : room.name }`);
-
- const userName = s.escapeHTML(RocketChat.settings.get('UI_Use_Real_Name') ? message.u.name || message.u.username : message.u.username);
-
- const header = TAPi18n.__(room.t === 'd' ? 'User_sent_a_message_to_you' : 'User_sent_a_message_on_channel', {
- username: userName,
- channel: roomName,
- lng
- });
-
- if (messageContent) {
- return `${ header }
${ messageContent }`;
- }
-
- if (message.file) {
- const fileHeader = TAPi18n.__(room.t === 'd' ? 'User_uploaded_a_file_to_you' : 'User_uploaded_a_file_on_channel', {
- username: userName,
- channel: roomName,
- lng
- });
-
- let content = `${ TAPi18n.__('Attachment_File_Uploaded') }: ${ s.escapeHTML(message.file.name) }`;
-
- if (message.attachments && message.attachments.length === 1 && message.attachments[0].description !== '') {
- content += `
${ s.escapeHTML(message.attachments[0].description) }`;
- }
-
- return `${ fileHeader }
${ content }`;
- }
-
- if (message.attachments.length > 0) {
- const [ attachment ] = message.attachments;
-
- let content = '';
-
- if (attachment.title) {
- content += `${ s.escapeHTML(attachment.title) }
`;
- }
- if (attachment.text) {
- content += `${ s.escapeHTML(attachment.text) }
`;
- }
-
- return `${ header }
${ content }`;
- }
-
- return header;
-}
-
-RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
- // skips this callback if the message was edited
- if (message.editedAt) {
- return message;
- }
-
- if (message.ts && Math.abs(moment(message.ts).diff()) > 60000) {
- return message;
- }
-
- const getMessageLink = (room, sub) => {
- const roomPath = RocketChat.roomTypes.getRouteLink(room.t, sub);
- const path = Meteor.absoluteUrl(roomPath ? roomPath.replace(/^\//, '') : '');
- const style = [
- 'color: #fff;',
- 'padding: 9px 12px;',
- 'border-radius: 4px;',
- 'background-color: #04436a;',
- 'text-decoration: none;'
- ].join(' ');
- const message = TAPi18n.__('Offline_Link_Message');
- return `
${ message }`;
- };
-
- const divisorMessage = '
';
-
- let messageHTML;
-
- if (message.msg !== '') {
- messageHTML = s.escapeHTML(message.msg);
- message = RocketChat.callbacks.run('renderMessage', message);
- if (message.tokens && message.tokens.length > 0) {
- message.tokens.forEach((token) => {
- token.text = token.text.replace(/([^\$])(\$[^\$])/gm, '$1$$$2');
- messageHTML = messageHTML.replace(token.token, token.text);
- });
- }
- messageHTML = messageHTML.replace(/\n/gm, '
');
- }
-
- const header = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Header') || '');
- let footer = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Footer') || '');
-
- const usersToSendEmail = {};
- if (room.t === 'd') {
- usersToSendEmail[message.rid.replace(message.u._id, '')] = 'direct';
- } else {
- let isMentionAll = message.mentions.find(mention => mention._id === 'all');
-
- if (isMentionAll) {
- const maxMembersForNotification = RocketChat.settings.get('Notifications_Max_Room_Members');
- if (maxMembersForNotification !== 0 && room.usernames.length > maxMembersForNotification) {
- isMentionAll = undefined;
- }
- }
-
- let query;
- if (isMentionAll) {
- // Query all users in room limited by the max room members setting
- query = RocketChat.models.Subscriptions.findByRoomId(room._id);
- } else {
- // Query only mentioned users, will be always a few users
- const userIds = message.mentions.map(mention => mention._id);
- query = RocketChat.models.Subscriptions.findByRoomIdAndUserIdsOrAllMessages(room._id, userIds);
- }
-
- query.forEach(sub => {
- if (sub.disableNotifications) {
- return delete usersToSendEmail[sub.u._id];
- }
-
- const { emailNotifications, muteGroupMentions } = sub;
-
- if (emailNotifications === 'nothing') {
- return delete usersToSendEmail[sub.u._id];
- }
-
- if (isMentionAll && muteGroupMentions) {
- return delete usersToSendEmail[sub.u._id];
- }
-
- const mentionedUser = isMentionAll || message.mentions.find(mention => mention._id === sub.u._id);
-
- if (emailNotifications === 'default' || emailNotifications == null) {
- if (mentionedUser) {
- return usersToSendEmail[sub.u._id] = 'default';
- }
- return delete usersToSendEmail[sub.u._id];
- }
-
- if (emailNotifications === 'mentions' && mentionedUser) {
- return usersToSendEmail[sub.u._id] = 'mention';
- }
-
- if (emailNotifications === 'all') {
- return usersToSendEmail[sub.u._id] = 'all';
- }
- });
- }
- const userIdsToSendEmail = Object.keys(usersToSendEmail);
-
- let defaultLink;
-
- const linkByUser = {};
- if (RocketChat.roomTypes.hasCustomLink(room.t)) {
- RocketChat.models.Subscriptions.findByRoomIdAndUserIds(room._id, userIdsToSendEmail).forEach((sub) => {
- linkByUser[sub.u._id] = getMessageLink(room, sub);
- });
- } else {
- defaultLink = getMessageLink(room, {
- name: room.name
- });
- }
-
- if (userIdsToSendEmail.length > 0) {
- const usersOfMention = RocketChat.models.Users.getUsersToSendOfflineEmail(userIdsToSendEmail).fetch();
-
- if (usersOfMention && usersOfMention.length > 0) {
- usersOfMention.forEach((user) => {
- const emailNotificationMode = RocketChat.getUserPreference(user, 'emailNotificationMode');
- if (usersToSendEmail[user._id] === 'default') {
- if (emailNotificationMode === 'all') { //Mention/DM
- usersToSendEmail[user._id] = 'mention';
- } else {
- return;
- }
- }
-
- if (usersToSendEmail[user._id] === 'direct') {
- const userEmailPreferenceIsDisabled = emailNotificationMode === 'disabled';
- const directMessageEmailPreference = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(message.rid, message.rid.replace(message.u._id, '')).emailNotifications;
-
- if (directMessageEmailPreference === 'nothing') {
- return;
- }
-
- if ((directMessageEmailPreference === 'default' || directMessageEmailPreference == null) && userEmailPreferenceIsDisabled) {
- return;
- }
- }
-
- // Checks if user is in the room he/she is mentioned (unless it's public channel)
- if (room.t !== 'c' && room.usernames.indexOf(user.username) === -1) {
- return;
- }
-
- // Footer in case direct reply is enabled.
- if (RocketChat.settings.get('Direct_Reply_Enable')) {
- footer = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Footer_Direct_Reply') || '');
- }
-
- let emailSubject;
- const username = RocketChat.settings.get('UI_Use_Real_Name') ? message.u.name : message.u.username;
- const roomName = RocketChat.settings.get('UI_Allow_room_names_with_special_chars') ? room.fname : room.name;
- switch (usersToSendEmail[user._id]) {
- case 'all':
- emailSubject = RocketChat.placeholders.replace(RocketChat.settings.get('Offline_Mention_All_Email'), {
- user: username,
- room: roomName || room.label
- });
- break;
- case 'direct':
- emailSubject = RocketChat.placeholders.replace(RocketChat.settings.get('Offline_DM_Email'), {
- user: username,
- room: roomName
- });
- break;
- case 'mention':
- emailSubject = RocketChat.placeholders.replace(RocketChat.settings.get('Offline_Mention_Email'), {
- user: username,
- room: roomName
- });
- break;
- }
- user.emails.some((email) => {
- if (email.verified) {
- const content = getEmailContent({
- messageContent: messageHTML,
- message,
- user,
- room
- });
- email = {
- to: email.address,
- subject: emailSubject,
- html: header + content + divisorMessage + (linkByUser[user._id] || defaultLink) + footer
- };
- // using user full-name/channel name in from address
- if (room.t === 'd') {
- email.from = `${ String(message.u.name).replace(/@/g, '%40').replace(/[<>,]/g, '') } <${ RocketChat.settings.get('From_Email') }>`;
- } else {
- email.from = `${ String(room.name).replace(/@/g, '%40').replace(/[<>,]/g, '') } <${ RocketChat.settings.get('From_Email') }>`;
- }
- // If direct reply enabled, email content with headers
- if (RocketChat.settings.get('Direct_Reply_Enable')) {
- email.headers = {
- // Reply-To header with format "username+messageId@domain"
- 'Reply-To': `${ RocketChat.settings.get('Direct_Reply_Username').split('@')[0].split(RocketChat.settings.get('Direct_Reply_Separator'))[0] }${ RocketChat.settings.get('Direct_Reply_Separator') }${ message._id }@${ RocketChat.settings.get('Direct_Reply_Username').split('@')[1] }`
- };
- }
-
- Meteor.defer(() => {
- Email.send(email);
- });
-
- return true;
- }
- });
- });
- }
- }
-
- return message;
-
-}, RocketChat.callbacks.priority.LOW, 'sendEmailOnMessage');
diff --git a/packages/rocketchat-lib/server/lib/sendNotificationsOnMessage.js b/packages/rocketchat-lib/server/lib/sendNotificationsOnMessage.js
index e3c811d19366..e36e02776134 100644
--- a/packages/rocketchat-lib/server/lib/sendNotificationsOnMessage.js
+++ b/packages/rocketchat-lib/server/lib/sendNotificationsOnMessage.js
@@ -1,168 +1,130 @@
-/* globals Push */
-import _ from 'underscore';
-import s from 'underscore.string';
import moment from 'moment';
-const CATEGORY_MESSAGE = 'MESSAGE';
-const CATEGORY_MESSAGE_NOREPLY = 'MESSAGE_NOREPLY';
-
-/**
- * Replaces @username with full name
- *
- * @param {string} message The message to replace
- * @param {object[]} mentions Array of mentions used to make replacements
- *
- * @returns {string}
- */
-function replaceMentionedUsernamesWithFullNames(message, mentions) {
- if (!mentions || !mentions.length) {
- return message;
+import { callJoinRoom, messageContainsHighlight } from '../functions/notifications/';
+import { sendEmail, shouldNotifyEmail } from '../functions/notifications/email';
+import { sendSinglePush, shouldNotifyMobile } from '../functions/notifications/mobile';
+import { notifyDesktopUser, shouldNotifyDesktop } from '../functions/notifications/desktop';
+import { notifyAudioUser, shouldNotifyAudio } from '../functions/notifications/audio';
+
+const sendNotification = ({
+ subscription,
+ sender,
+ hasMentionToAll,
+ hasMentionToHere,
+ message,
+ room,
+ mentionIds,
+ disableAllMessageNotifications
+}) => {
+
+ // don't notify the sender
+ if (subscription.u._id === sender._id) {
+ return;
}
- mentions.forEach((mention) => {
- const user = RocketChat.models.Users.findOneById(mention._id);
- if (user && user.name) {
- message = message.replace(`@${ mention.username }`, user.name);
- }
- });
- return message;
-}
-function canSendMessageToRoom(room, username) {
- return !((room.muted || []).includes(username));
-}
-
-/**
- * This function returns a string ready to be shown in the notification
- *
- * @param {object} message the message to be parsed
- */
-function parseMessageText(message, userId) {
- const user = RocketChat.models.Users.findOneById(userId);
- const lng = user && user.language || RocketChat.settings.get('language') || 'en';
-
- if (!message.msg && message.attachments && message.attachments[0]) {
- message.msg = message.attachments[0].image_type ? TAPi18n.__('User_uploaded_image', {lng}) : TAPi18n.__('User_uploaded_file', {lng});
+ // notifications disabled
+ if (subscription.disableNotifications) {
+ return;
}
- message.msg = RocketChat.callbacks.run('beforeNotifyUser', message.msg);
- return message.msg;
-}
-/**
- * Send notification to user
- *
- * @param {string} userId The user to notify
- * @param {object} user The sender
- * @param {object} room The room send from
- * @param {number} duration Duration of notification
- */
-function notifyDesktopUser(userId, user, message, room, duration) {
-
- const UI_Use_Real_Name = RocketChat.settings.get('UI_Use_Real_Name') === true;
- message.msg = parseMessageText(message, userId);
-
- if (UI_Use_Real_Name) {
- message.msg = replaceMentionedUsernamesWithFullNames(message.msg, message.mentions);
+ // dont send notification to users who ignored the sender
+ if (Array.isArray(subscription.ignored) && subscription.ignored.find(sender._id)) {
+ return;
}
- let title = '';
- let text = '';
- if (room.t === 'd') {
- title = UI_Use_Real_Name ? user.name : `@${ user.username }`;
- text = message.msg;
- } else if (room.name) {
- title = `#${ room.name }`;
- text = `${ user.username }: ${ message.msg }`;
- }
+ const hasMentionToUser = mentionIds.includes(subscription.u._id);
- if (title === '' || text === '') {
+ // mute group notifications (@here and @all) if not directly mentioned as well
+ if (!hasMentionToUser && subscription.muteGroupMentions && (hasMentionToAll || hasMentionToHere)) {
return;
}
- RocketChat.Notifications.notifyUser(userId, 'notification', {
- title,
- text,
- duration,
- payload: {
- _id: message._id,
- rid: message.rid,
- sender: message.u,
- type: room.t,
- name: room.name
- }
- });
-}
+ const receiver = RocketChat.models.Users.findOneById(subscription.u._id);
-function notifyAudioUser(userId, message, room) {
- RocketChat.Notifications.notifyUser(userId, 'audioNotification', {
- payload: {
- _id: message._id,
- rid: message.rid,
- sender: message.u,
- type: room.t,
- name: room.name
- }
- });
-}
+ if (!receiver || !receiver.active) {
+ return;
+ }
-/**
- * Checks if a message contains a user highlight
- *
- * @param {string} message
- * @param {array|undefined} highlights
- *
- * @returns {boolean}
- */
-function messageContainsHighlight(message, highlights) {
- if (! highlights || highlights.length === 0) { return false; }
-
- let has = false;
- highlights.some(function(highlight) {
- const regexp = new RegExp(s.escapeRegExp(highlight), 'i');
- if (regexp.test(message.msg)) {
- has = true;
- return true;
- }
- });
-
- return has;
-}
+ const isHighlighted = messageContainsHighlight(message, receiver.settings && receiver.settings.preferences && receiver.settings.preferences.highlights);
+
+ const {
+ audioNotifications,
+ desktopNotifications,
+ mobilePushNotifications,
+ emailNotifications
+ } = subscription;
+
+ let notificationSent = false;
+
+ // busy users don't receive audio notification
+ if (shouldNotifyAudio({
+ disableAllMessageNotifications,
+ status: receiver.status,
+ audioNotifications,
+ hasMentionToAll,
+ hasMentionToHere,
+ isHighlighted,
+ hasMentionToUser
+ })) {
+ notifyAudioUser(subscription.u._id, message, room);
+ }
-function getBadgeCount(userId) {
- const subscriptions = RocketChat.models.Subscriptions.findUnreadByUserId(userId).fetch();
+ // busy users don't receive desktop notification
+ if (shouldNotifyDesktop({
+ disableAllMessageNotifications,
+ status: receiver.status,
+ desktopNotifications,
+ hasMentionToAll,
+ hasMentionToHere,
+ isHighlighted,
+ hasMentionToUser
+ })) {
+ notificationSent = true;
+ notifyDesktopUser(subscription.u._id, sender, message, room, subscription.desktopNotificationDuration);
+ }
- return subscriptions.reduce((unread, sub) => {
- return sub.unread + unread;
- }, 0);
-}
+ if (shouldNotifyMobile({
+ disableAllMessageNotifications,
+ mobilePushNotifications,
+ hasMentionToAll,
+ isHighlighted,
+ hasMentionToUser,
+ statusConnection: receiver.statusConnection
+ })) {
+ notificationSent = true;
+
+ sendSinglePush({
+ room,
+ message,
+ userId: subscription.u._id,
+ senderUsername: sender.username,
+ receiverUsername: receiver.username
+ });
+ }
-const sendPushNotifications = (userIdsToPushNotify = [], message, room, push_room, push_username, push_message, pushUsernames) => {
- if (userIdsToPushNotify.length > 0 && Push.enabled === true) {
- // send a push notification for each user individually (to get his/her badge count)
- userIdsToPushNotify.forEach((userIdToNotify) => {
- RocketChat.PushNotification.send({
- roomId: message.rid,
- roomName: push_room,
- username: push_username,
- message: push_message,
- badge: getBadgeCount(userIdToNotify),
- payload: {
- host: Meteor.absoluteUrl(),
- rid: message.rid,
- sender: message.u,
- type: room.t,
- name: room.name
- },
- usersTo: {
- userId: userIdToNotify
- },
- category: canSendMessageToRoom(room, pushUsernames[userIdToNotify]) ? CATEGORY_MESSAGE : CATEGORY_MESSAGE_NOREPLY
- });
+ if (receiver.emails && shouldNotifyEmail({
+ disableAllMessageNotifications,
+ statusConnection: receiver.statusConnection,
+ emailNotifications,
+ isHighlighted,
+ hasMentionToUser,
+ hasMentionToAll
+ })) {
+ receiver.emails.some((email) => {
+ if (email.verified) {
+ sendEmail({ message, receiver, subscription, room, emailAddress: email.address });
+
+ return true;
+ }
});
}
+
+ if (notificationSent) {
+ RocketChat.Sandstorm.notify(message, [subscription.u._id], `@${ sender.username }: ${ message.msg }`, room.t === 'p' ? 'privateMessage' : 'message');
+ }
};
-const callJoin = (user, rid) => user.active && Meteor.runAsUser(user._id, () => Meteor.call('joinRoom', rid));
-RocketChat.callbacks.add('afterSaveMessage', function(message, room, userId) {
+function sendAllNotifications(message, room) {
// skips this callback if the message was edited
if (message.editedAt) {
@@ -173,312 +135,80 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room, userId) {
return message;
}
- const pushUsernames = {};
-
- const user = (room.t !== 'l') ? RocketChat.models.Users.findOneById(message.u._id) : room.v;
-
- if (!user) {
+ if (!room || room.t == null) {
return message;
}
- /*
- Increment unread couter if direct messages
- */
- const settings = {
- alwaysNotifyDesktopUsers: [],
- dontNotifyDesktopUsers: [],
- alwaysNotifyMobileUsers: [],
- dontNotifyMobileUsers: [],
- desktopNotificationDurations: {},
- alwaysNotifyAudioUsers: [],
- dontNotifyAudioUsers: [],
- audioNotificationValues: {},
- dontNotifyUsersOnGroupMentions: []
- };
-
- /**
- * Checks if a given user can be notified
- *
- * @param {string} id
- * @param {string} type - mobile|desktop
- *
- * @returns {boolean}
- */
- function canBeNotified(id, type) {
- const types = {
- desktop: [ 'dontNotifyDesktopUsers', 'alwaysNotifyDesktopUsers' ],
- mobile: [ 'dontNotifyMobileUsers', 'alwaysNotifyMobileUsers' ],
- audio: [ 'dontNotifyAudioUsers', 'alwaysNotifyAudioUsers' ]
- };
-
- return (settings[types[type][0]].indexOf(id) === -1 || settings[types[type][1]].indexOf(id) !== -1);
+ const sender = (room.t !== 'l') ? RocketChat.models.Users.findOneById(message.u._id) : room.v;
+ if (!sender) {
+ return message;
}
// Don't fetch all users if room exceeds max members
const maxMembersForNotification = RocketChat.settings.get('Notifications_Max_Room_Members');
const disableAllMessageNotifications = room.usernames.length > maxMembersForNotification && maxMembersForNotification !== 0;
- const subscriptions = RocketChat.models.Subscriptions.findNotificationPreferencesByRoom(room._id, disableAllMessageNotifications) || [];
- const userIds = [];
- subscriptions.forEach(s => userIds.push(s.u._id));
- const users = {};
-
- RocketChat.models.Users.findUsersByIds(userIds, { fields: { 'settings.preferences': 1 } }).forEach((user) => {
- users[user._id] = user;
- });
-
- subscriptions.forEach(subscription => {
- if (subscription.disableNotifications) {
- settings.dontNotifyDesktopUsers.push(subscription.u._id);
- settings.dontNotifyMobileUsers.push(subscription.u._id);
- settings.dontNotifyAudioUsers.push(subscription.u._id);
- return;
- }
-
- if (Array.isArray(subscription.ignored) && subscription.ignored.find(message.u._id)) {
- return;
- }
-
- const {
- audioNotifications = RocketChat.getUserPreference(users[subscription.u._id], 'audioNotifications'),
- desktopNotifications = RocketChat.getUserPreference(users[subscription.u._id], 'desktopNotifications'),
- mobilePushNotifications = RocketChat.getUserPreference(users[subscription.u._id], 'mobileNotifications')
- } = subscription;
-
- if (audioNotifications === 'all' && !disableAllMessageNotifications) {
- settings.alwaysNotifyAudioUsers.push(subscription.u._id);
- }
- if (desktopNotifications === 'all' && !disableAllMessageNotifications) {
- settings.alwaysNotifyDesktopUsers.push(subscription.u._id);
- } else if (desktopNotifications === 'nothing') {
- settings.dontNotifyDesktopUsers.push(subscription.u._id);
- }
- if (mobilePushNotifications === 'all' && !disableAllMessageNotifications) {
- settings.alwaysNotifyMobileUsers.push(subscription.u._id);
- } else if (mobilePushNotifications === 'nothing') {
- settings.dontNotifyMobileUsers.push(subscription.u._id);
- }
-
- settings.audioNotificationValues[subscription.u._id] = subscription.audioNotificationValue;
- settings.desktopNotificationDurations[subscription.u._id] = subscription.desktopNotificationDuration;
-
- if (subscription.muteGroupMentions) {
- settings.dontNotifyUsersOnGroupMentions.push(subscription.u._id);
- }
- });
- let userIdsForAudio = [];
- let userIdsToNotify = [];
- let userIdsToPushNotify = [];
- const mentions = [];
- const alwaysNotifyMobileBoolean = RocketChat.settings.get('Notifications_Always_Notify_Mobile');
-
- const usersWithHighlights = RocketChat.models.Users.findUsersByUsernamesWithHighlights(room.usernames, { fields: { '_id': 1, 'settings.preferences.highlights': 1 }}).fetch()
- .filter(user => messageContainsHighlight(message, user.settings.preferences.highlights));
-
- let push_message = ' ';
- //Set variables depending on Push Notification settings
- if (RocketChat.settings.get('Push_show_message')) {
- push_message = parseMessageText(message, userId);
- }
-
- let push_username = '';
- let push_room = '';
- if (RocketChat.settings.get('Push_show_username_room')) {
- push_username = user.username;
- push_room = `#${ room.name }`;
- }
-
- if (room.t == null || room.t === 'd') {
- const userOfMentionId = message.rid.replace(message.u._id, '');
- const userOfMention = RocketChat.models.Users.findOne({
- _id: userOfMentionId
- }, {
- fields: {
- username: 1,
- statusConnection: 1
- }
- });
-
- // Always notify Sandstorm
- if (userOfMention != null) {
- RocketChat.Sandstorm.notify(message, [userOfMention._id],
- `@${ user.username }: ${ message.msg }`, 'privateMessage');
-
- if (canBeNotified(userOfMentionId, 'desktop')) {
- const duration = settings.desktopNotificationDurations[userOfMention._id];
- notifyDesktopUser(userOfMention._id, user, message, room, duration);
- }
-
- if (canBeNotified(userOfMentionId, 'mobile')) {
- if (Push.enabled === true && (userOfMention.statusConnection !== 'online' || alwaysNotifyMobileBoolean === true)) {
- RocketChat.PushNotification.send({
- roomId: message.rid,
- username: push_username,
- message: push_message,
- badge: getBadgeCount(userOfMention._id),
- payload: {
- host: Meteor.absoluteUrl(),
- rid: message.rid,
- sender: message.u,
- type: room.t,
- name: room.name
- },
- usersTo: {
- userId: userOfMention._id
- },
- category: canSendMessageToRoom(room, userOfMention.username) ? CATEGORY_MESSAGE : CATEGORY_MESSAGE_NOREPLY
- });
- return message;
- }
- }
- }
+ // the find bellow is crucial. all subscription records returned will receive at least one kind of notification.
+ // the query is defined by the server's default values and Notifications_Max_Room_Members setting.
+ let subscriptions;
+ if (disableAllMessageNotifications) {
+ subscriptions = RocketChat.models.Subscriptions.findAllMessagesNotificationPreferencesByRoom(room._id);
} else {
- const mentionIds = (message.mentions || []).map(({_id}) => _id);
- const toAll = mentionIds.includes('all');
- const toHere = mentionIds.includes('here');
-
- if (mentionIds.length + settings.alwaysNotifyDesktopUsers.length > 0) {
- let desktopMentionIds = _.union(mentionIds, settings.alwaysNotifyDesktopUsers);
- desktopMentionIds = _.difference(desktopMentionIds, settings.dontNotifyDesktopUsers);
-
- let usersOfDesktopMentions = RocketChat.models.Users.find({
- _id: {
- $in: desktopMentionIds
- }
- }, {
- fields: {
- _id: 1,
- username: 1,
- active: 1
- }
- }).fetch();
- mentions.push(...usersOfDesktopMentions);
- if (room.t !== 'c') {
- usersOfDesktopMentions = _.reject(usersOfDesktopMentions, (usersOfMentionItem) => {
- return room.usernames.indexOf(usersOfMentionItem.username) === -1;
- });
- }
-
- userIdsToNotify = _.pluck(usersOfDesktopMentions, '_id');
- }
-
- if (mentionIds.length + settings.alwaysNotifyMobileUsers.length > 0) {
- let mobileMentionIds = _.union(mentionIds, settings.alwaysNotifyMobileUsers);
- mobileMentionIds = _.difference(mobileMentionIds, settings.dontNotifyMobileUsers);
-
- const usersOfMobileMentionsQuery = {
- _id: {
- $in: mobileMentionIds
- }
- };
-
- if (alwaysNotifyMobileBoolean !== true) {
- usersOfMobileMentionsQuery.statusConnection = { $ne: 'online' };
- }
-
- let usersOfMobileMentions = RocketChat.models.Users.find(usersOfMobileMentionsQuery, {
- fields: {
- _id: 1,
- username: 1,
- statusConnection: 1,
- active: 1
- }
- }).fetch();
-
- mentions.push(...usersOfMobileMentions);
- if (room.t !== 'c') {
- usersOfMobileMentions = _.reject(usersOfMobileMentions, usersOfMentionItem => !room.usernames.includes(usersOfMentionItem.username));
- }
+ const mentionsFilter = { $in: ['all', 'mentions'] };
+ const excludesNothingFilter = { $ne: 'nothing' };
+
+ // evaluate if doing three specific finds is better than evaluting all results
+ subscriptions = RocketChat.models.Subscriptions.findNotificationPreferencesByRoom({
+ roomId: room._id,
+ desktopFilter: RocketChat.settings.get('Accounts_Default_User_Preferences_desktopNotifications') === 'nothing' ? mentionsFilter : excludesNothingFilter,
+ emailFilter: RocketChat.settings.get('Accounts_Default_User_Preferences_emailNotificationMode') === 'disabled' ? mentionsFilter : excludesNothingFilter,
+ mobileFilter: RocketChat.settings.get('Accounts_Default_User_Preferences_mobileNotifications') === 'nothing' ? mentionsFilter : excludesNothingFilter
+ });
+ }
- userIdsToPushNotify = usersOfMobileMentions.map(userMobile => {
- pushUsernames[userMobile._id] = userMobile.username;
- return userMobile._id;
- });
- }
-
- if (mentionIds.length + settings.alwaysNotifyAudioUsers.length > 0) {
- let audioMentionIds = _.union(mentionIds, settings.alwaysNotifyAudioUsers);
- audioMentionIds = _.difference(audioMentionIds, userIdsToNotify);
-
- let usersOfAudioMentions = RocketChat.models.Users.find({ _id: { $in: audioMentionIds }, statusConnection: {
- $ne:'offline'
- } }, {
- fields: {
- _id: 1,
- username: 1,
- active: 1
- }
- }).fetch();
- mentions.push(...usersOfAudioMentions);
- if (room.t !== 'c') {
- usersOfAudioMentions = _.reject(usersOfAudioMentions, (usersOfMentionItem) => {
- return room.usernames.indexOf(usersOfMentionItem.username) === -1;
+ const mentionIds = (message.mentions || []).map(({_id}) => _id);
+ const hasMentionToAll = mentionIds.includes('all');
+ const hasMentionToHere = mentionIds.includes('here');
+
+ subscriptions.forEach((subscription) => sendNotification({
+ subscription,
+ sender,
+ hasMentionToAll,
+ hasMentionToHere,
+ message,
+ room,
+ mentionIds,
+ disableAllMessageNotifications
+ }));
+
+ // on public channels, if a mentioned user is not member of the channel yet, he will first join the channel and then be notified based on his preferences.
+ if (room.t === 'c') {
+ Promise.all(message.mentions
+ .filter(({ _id, username }) => _id !== 'here' && _id !== 'all' && !room.usernames.includes(username))
+ .map(async(user) => {
+ await callJoinRoom(user, room._id);
+
+ return user._id;
+ })
+ ).then((users) => {
+ users.forEach((userId) => {
+ const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, userId);
+
+ sendNotification({
+ subscription,
+ sender,
+ hasMentionToAll,
+ hasMentionToHere,
+ message,
+ room,
+ mentionIds
});
- }
-
- userIdsForAudio = _.pluck(usersOfAudioMentions, '_id');
- }
-
- if (room.t === 'c') {
- mentions.filter(user => !room.usernames.includes(user.username))
- .forEach(user =>callJoin(user, room._id));
- }
-
- if ([toAll, toHere].some(e => e) && room.usernames && room.usernames.length > 0) {
- RocketChat.models.Users.find({
- username: { $in: room.usernames },
- _id: { $ne: user._id }
- }, {
- fields: {
- _id: 1,
- username: 1,
- status: 1,
- statusConnection: 1
- }
- }).forEach(function({ status, _id, username, statusConnection }) { // user
- if (Array.isArray(settings.dontNotifyUsersOnGroupMentions) && settings.dontNotifyUsersOnGroupMentions.includes(_id)) {
- return;
- }
-
- if (['online', 'away', 'busy'].includes(status) && !(settings.dontNotifyDesktopUsers || []).includes(_id)) {
- userIdsToNotify.push(_id);
- userIdsForAudio.push(_id);
- }
- if (toAll && statusConnection !== 'online' && !(settings.dontNotifyMobileUsers || []).includes(_id)) {
- pushUsernames[_id] = username;
- return userIdsToPushNotify.push(_id);
- }
- if (toAll && statusConnection !== 'online') {
- userIdsForAudio.push(_id);
- }
});
- }
-
- if (usersWithHighlights.length > 0) {
- const highlightsIds = _.pluck(usersWithHighlights, '_id');
- userIdsForAudio = userIdsForAudio.concat(highlightsIds);
- userIdsToNotify = userIdsToNotify.concat(highlightsIds);
- userIdsToPushNotify = userIdsToPushNotify.concat(highlightsIds);
- }
-
- userIdsToNotify = _.without(_.compact(_.unique(userIdsToNotify)), message.u._id);
- userIdsToPushNotify = _.without(_.compact(_.unique(userIdsToPushNotify)), message.u._id);
- userIdsForAudio = _.without(_.compact(_.unique(userIdsForAudio)), message.u._id);
-
- for (const usersOfMentionId of userIdsToNotify) {
- const duration = settings.desktopNotificationDurations[usersOfMentionId];
- notifyDesktopUser(usersOfMentionId, user, message, room, duration);
- }
- for (const usersOfMentionId of userIdsForAudio) {
- notifyAudioUser(usersOfMentionId, message, room);
- }
- sendPushNotifications(userIdsToPushNotify, message, room, push_room, push_username, push_message, pushUsernames);
-
- const allUserIdsToNotify = _.unique(userIdsToNotify.concat(userIdsToPushNotify));
- RocketChat.Sandstorm.notify(message, allUserIdsToNotify,
- `@${ user.username }: ${ message.msg }`, room.t === 'p' ? 'privateMessage' : 'message');
+ });
}
return message;
+}
+
+RocketChat.callbacks.add('afterSaveMessage', sendAllNotifications, RocketChat.callbacks.priority.LOW, 'sendNotificationsOnMessage');
-}, RocketChat.callbacks.priority.LOW, 'sendNotificationOnMessage');
diff --git a/packages/rocketchat-lib/server/models/Subscriptions.js b/packages/rocketchat-lib/server/models/Subscriptions.js
index 116751db301d..32881ca97283 100644
--- a/packages/rocketchat-lib/server/models/Subscriptions.js
+++ b/packages/rocketchat-lib/server/models/Subscriptions.js
@@ -11,7 +11,9 @@ class ModelSubscriptions extends RocketChat.models._Base {
this.tryEnsureIndex({ 'u._id': 1, 'name': 1, 't': 1, 'code': 1 }, { unique: 1 });
this.tryEnsureIndex({ 'open': 1 });
this.tryEnsureIndex({ 'alert': 1 });
- this.tryEnsureIndex({ 'unread': 1 });
+
+ this.tryEnsureIndex({ rid: 1, 'u._id': 1, open: 1 });
+
this.tryEnsureIndex({ 'ts': 1 });
this.tryEnsureIndex({ 'ls': 1 });
this.tryEnsureIndex({ 'audioNotifications': 1 }, { sparse: 1 });
@@ -475,15 +477,28 @@ class ModelSubscriptions extends RocketChat.models._Base {
'u._id': {
$ne: userId
},
- $or: [
- { alert: { $ne: true } },
- { open: { $ne: true } }
- ]
+ alert: { $ne: true }
+ };
+
+ const update = {
+ $set: {
+ alert: true
+ }
+ };
+ return this.update(query, update, { multi: true });
+ }
+
+ setOpenForRoomIdExcludingUserId(roomId, userId) {
+ const query = {
+ rid: roomId,
+ 'u._id': {
+ $ne: userId
+ },
+ open: { $ne: true }
};
const update = {
$set: {
- alert: true,
open: true
}
};
@@ -596,6 +611,108 @@ class ModelSubscriptions extends RocketChat.models._Base {
return this.update(query, update, { multi: true });
}
+ clearDesktopNotificationUserPreferences(userId) {
+ const query = {
+ 'u._id': userId,
+ desktopPrefOrigin: 'user'
+ };
+
+ const update = {
+ $unset: {
+ desktopNotifications: 1,
+ desktopPrefOrigin: 1
+ }
+ };
+
+ return this.update(query, update, { multi: true });
+ }
+
+ updateDesktopNotificationUserPreferences(userId, desktopNotifications) {
+ const query = {
+ 'u._id': userId,
+ desktopPrefOrigin: {
+ $ne: 'subscription'
+ }
+ };
+
+ const update = {
+ $set: {
+ desktopNotifications,
+ desktopPrefOrigin: 'user'
+ }
+ };
+
+ return this.update(query, update, { multi: true });
+ }
+
+ clearMobileNotificationUserPreferences(userId) {
+ const query = {
+ 'u._id': userId,
+ mobilePrefOrigin: 'user'
+ };
+
+ const update = {
+ $unset: {
+ mobilePushNotifications: 1,
+ mobilePrefOrigin: 1
+ }
+ };
+
+ return this.update(query, update, { multi: true });
+ }
+
+ updateMobileNotificationUserPreferences(userId, mobilePushNotifications) {
+ const query = {
+ 'u._id': userId,
+ mobilePrefOrigin: {
+ $ne: 'subscription'
+ }
+ };
+
+ const update = {
+ $set: {
+ mobilePushNotifications,
+ mobilePrefOrigin: 'user'
+ }
+ };
+
+ return this.update(query, update, { multi: true });
+ }
+
+ clearEmailNotificationUserPreferences(userId) {
+ const query = {
+ 'u._id': userId,
+ emailPrefOrigin: 'user'
+ };
+
+ const update = {
+ $unset: {
+ emailNotifications: 1,
+ emailPrefOrigin: 1
+ }
+ };
+
+ return this.update(query, update, { multi: true });
+ }
+
+ updateEmailNotificationUserPreferences(userId, emailNotifications) {
+ const query = {
+ 'u._id': userId,
+ emailPrefOrigin: {
+ $ne: 'subscription'
+ }
+ };
+
+ const update = {
+ $set: {
+ emailNotifications,
+ emailPrefOrigin: 'user'
+ }
+ };
+
+ return this.update(query, update, { multi: true });
+ }
+
// INSERT
createWithRoomAndUser(room, user, extraData) {
const subscription = {
@@ -617,6 +734,27 @@ class ModelSubscriptions extends RocketChat.models._Base {
}
};
+ const {
+ desktopNotifications,
+ mobileNotifications,
+ emailNotificationMode
+ } = (user.settings && user.settings.preferences) || {};
+
+ if (desktopNotifications && desktopNotifications !== 'default') {
+ subscription.desktopNotifications = desktopNotifications;
+ subscription.desktopPrefOrigin = 'user';
+ }
+
+ if (mobileNotifications && mobileNotifications !== 'default') {
+ subscription.mobilePushNotifications = mobileNotifications;
+ subscription.mobilePrefOrigin = 'user';
+ }
+
+ if (emailNotificationMode && emailNotificationMode !== 'default') {
+ subscription.emailNotifications = emailNotificationMode === 'disabled' ? 'nothing' : user.settings.preferences.emailNotificationMode;
+ subscription.emailPrefOrigin = 'user';
+ }
+
_.extend(subscription, extraData);
return this.insert(subscription);
diff --git a/packages/rocketchat-lib/server/startup/settings.js b/packages/rocketchat-lib/server/startup/settings.js
index 9434185df598..5d21ad446de4 100644
--- a/packages/rocketchat-lib/server/startup/settings.js
+++ b/packages/rocketchat-lib/server/startup/settings.js
@@ -180,7 +180,7 @@ RocketChat.settings.addGroup('Accounts', function() {
});
this.section('Accounts_Default_User_Preferences', function() {
- this.add('Accounts_Default_User_Preferences_enableAutoAway', false, {
+ this.add('Accounts_Default_User_Preferences_enableAutoAway', true, {
type: 'boolean',
'public': true,
i18nLabel: 'Enable_Auto_Away'
diff --git a/packages/rocketchat-push-notifications/server/methods/saveNotificationSettings.js b/packages/rocketchat-push-notifications/server/methods/saveNotificationSettings.js
index c4d3cdb3dbbb..314cc081601d 100644
--- a/packages/rocketchat-push-notifications/server/methods/saveNotificationSettings.js
+++ b/packages/rocketchat-push-notifications/server/methods/saveNotificationSettings.js
@@ -12,13 +12,35 @@ Meteor.methods({
updateMethod: (subscription, value) => RocketChat.models.Subscriptions.updateAudioNotificationsById(subscription._id, value)
},
'desktopNotifications': {
- updateMethod: (subscription, value) => RocketChat.models.Subscriptions.updateDesktopNotificationsById(subscription._id, value)
+ updateMethod: (subscription, value) => {
+ if (value === 'default') {
+ const userPref = RocketChat.getUserNotificationPreference(Meteor.userId(), 'desktop');
+ RocketChat.models.Subscriptions.updateDesktopNotificationsById(subscription._id, userPref.origin === 'server' ? null : userPref);
+ } else {
+ RocketChat.models.Subscriptions.updateDesktopNotificationsById(subscription._id, { value, origin: 'subscription' });
+ }
+ }
},
'mobilePushNotifications': {
- updateMethod: (subscription, value) => RocketChat.models.Subscriptions.updateMobilePushNotificationsById(subscription._id, value)
+ updateMethod: (subscription, value) => {
+ if (value === 'default') {
+ const userPref = RocketChat.getUserNotificationPreference(Meteor.userId(), 'mobile');
+ RocketChat.models.Subscriptions.updateMobilePushNotificationsById(subscription._id, userPref.origin === 'server' ? null : userPref);
+ } else {
+ RocketChat.models.Subscriptions.updateMobilePushNotificationsById(subscription._id, { value, origin: 'subscription' });
+ }
+ }
},
'emailNotifications': {
- updateMethod: (subscription, value) => RocketChat.models.Subscriptions.updateEmailNotificationsById(subscription._id, value)
+ updateMethod: (subscription, value) => {
+ if (value === 'default') {
+ const userPref = RocketChat.getUserNotificationPreference(Meteor.userId(), 'email');
+ userPref.value = userPref.value === 'disabled' ? 'nothing' : userPref.value;
+ RocketChat.models.Subscriptions.updateEmailNotificationsById(subscription._id, userPref.origin === 'server' ? null : userPref);
+ } else {
+ RocketChat.models.Subscriptions.updateEmailNotificationsById(subscription._id, { value, origin: 'subscription' });
+ }
+ }
},
'unreadAlert': {
updateMethod: (subscription, value) => RocketChat.models.Subscriptions.updateUnreadAlertById(subscription._id, value)
diff --git a/packages/rocketchat-push-notifications/server/models/Subscriptions.js b/packages/rocketchat-push-notifications/server/models/Subscriptions.js
index ce988d39a332..c769297354c8 100644
--- a/packages/rocketchat-push-notifications/server/models/Subscriptions.js
+++ b/packages/rocketchat-push-notifications/server/models/Subscriptions.js
@@ -35,10 +35,16 @@ RocketChat.models.Subscriptions.updateDesktopNotificationsById = function(_id, d
const update = {};
- if (desktopNotifications === 'default') {
- update.$unset = { desktopNotifications: 1 };
+ if (desktopNotifications === null) {
+ update.$unset = {
+ desktopNotifications: 1,
+ desktopPrefOrigin: 1
+ };
} else {
- update.$set = { desktopNotifications };
+ update.$set = {
+ desktopNotifications: desktopNotifications.value,
+ desktopPrefOrigin: desktopNotifications.origin
+ };
}
return this.update(query, update);
@@ -65,10 +71,16 @@ RocketChat.models.Subscriptions.updateMobilePushNotificationsById = function(_id
const update = {};
- if (mobilePushNotifications === 'default') {
- update.$unset = { mobilePushNotifications: 1 };
+ if (mobilePushNotifications === null) {
+ update.$unset = {
+ mobilePushNotifications: 1,
+ mobilePrefOrigin: 1
+ };
} else {
- update.$set = { mobilePushNotifications };
+ update.$set = {
+ mobilePushNotifications: mobilePushNotifications.value,
+ mobilePrefOrigin: mobilePushNotifications.origin
+ };
}
return this.update(query, update);
@@ -79,11 +91,19 @@ RocketChat.models.Subscriptions.updateEmailNotificationsById = function(_id, ema
_id
};
- const update = {
- $set: {
- emailNotifications
- }
- };
+ const update = {};
+
+ if (emailNotifications === null) {
+ update.$unset = {
+ emailNotifications: 1,
+ emailPrefOrigin: 1
+ };
+ } else {
+ update.$set = {
+ emailNotifications: emailNotifications.value,
+ emailPrefOrigin: emailNotifications.origin
+ };
+ }
return this.update(query, update);
};
@@ -189,27 +209,6 @@ RocketChat.models.Subscriptions.findDontNotifyMobileUsersByRoomId = function(roo
return this.find(query);
};
-RocketChat.models.Subscriptions.findNotificationPreferencesByRoom = function(roomId, explicit) {
- const query = {
- rid: roomId,
- 'u._id': {$exists: true}
- };
-
- if (explicit) {
- query.$or = [
- {audioNotifications: {$exists: true}},
- {audioNotificationValue: {$exists: true}},
- {desktopNotifications: {$exists: true}},
- {desktopNotificationDuration: {$exists: true}},
- {mobilePushNotifications: {$exists: true}},
- {disableNotifications: {$exists: true}},
- {muteGroupMentions: {$exists: true}}
- ];
- }
-
- return this.find(query, { fields: { 'u._id': 1, audioNotifications: 1, audioNotificationValue: 1, desktopNotificationDuration: 1, desktopNotifications: 1, mobilePushNotifications: 1, disableNotifications: 1, muteGroupMentions: 1 } });
-};
-
RocketChat.models.Subscriptions.findWithSendEmailByRoomId = function(roomId) {
const query = {
rid: roomId,
@@ -220,3 +219,56 @@ RocketChat.models.Subscriptions.findWithSendEmailByRoomId = function(roomId) {
return this.find(query, { fields: { emailNotifications: 1, u: 1 } });
};
+
+
+RocketChat.models.Subscriptions.findNotificationPreferencesByRoom = function({ roomId: rid, desktopFilter: desktopNotifications, mobileFilter: mobilePushNotifications, emailFilter: emailNotifications }) {
+ const query = {
+ rid,
+ 'u._id': {$exists: true},
+ $or: [
+ { desktopNotifications },
+ { mobilePushNotifications },
+ { emailNotifications }
+ ]
+ };
+
+ return this._db.find(query, {
+ fields: {
+ 'u._id': 1,
+ audioNotifications: 1,
+ audioNotificationValue: 1,
+ desktopNotificationDuration: 1,
+ desktopNotifications: 1,
+ mobilePushNotifications: 1,
+ emailNotifications: 1,
+ disableNotifications: 1,
+ muteGroupMentions: 1
+ }
+ });
+};
+
+RocketChat.models.Subscriptions.findAllMessagesNotificationPreferencesByRoom = function(roomId) {
+ const query = {
+ rid: roomId,
+ 'u._id': {$exists: true},
+ $or: [
+ { desktopNotifications: { $in: ['all', 'mentions'] } },
+ { mobilePushNotifications: { $in: ['all', 'mentions'] } },
+ { emailNotifications: { $in: ['all', 'mentions'] } }
+ ]
+ };
+
+ return this._db.find(query, {
+ fields: {
+ 'u._id': 1,
+ audioNotifications: 1,
+ audioNotificationValue: 1,
+ desktopNotificationDuration: 1,
+ desktopNotifications: 1,
+ mobilePushNotifications: 1,
+ emailNotifications: 1,
+ disableNotifications: 1,
+ muteGroupMentions: 1
+ }
+ });
+};
diff --git a/packages/rocketchat-ui-account/client/accountPreferences.html b/packages/rocketchat-ui-account/client/accountPreferences.html
index ac820da9c198..4e4e64807519 100644
--- a/packages/rocketchat-ui-account/client/accountPreferences.html
+++ b/packages/rocketchat-ui-account/client/accountPreferences.html
@@ -107,6 +107,7 @@ {{_ "Notifications"}}
@@ -319,7 +320,7 @@
{{_ "My Data"}}
-
+
{{/if}}
diff --git a/packages/rocketchat-ui-account/client/accountPreferences.js b/packages/rocketchat-ui-account/client/accountPreferences.js
index 9dcbb99ff2b6..937e66eb6897 100644
--- a/packages/rocketchat-ui-account/client/accountPreferences.js
+++ b/packages/rocketchat-ui-account/client/accountPreferences.js
@@ -9,6 +9,11 @@ const notificationLabels = {
nothing: 'Nothing'
};
+const emailLabels = {
+ disabled: 'Email_Notification_Mode_Disabled',
+ all: 'Email_Notification_Mode_All'
+};
+
function checkedSelected(property, value, defaultValue=undefined) {
if (defaultValue && defaultValue.hash) {
defaultValue = undefined;
@@ -84,6 +89,9 @@ Template.accountPreferences.helpers({
defaultMobileNotification() {
return notificationLabels[RocketChat.settings.get('Accounts_Default_User_Preferences_mobileNotifications')];
},
+ defaultEmailNotification() {
+ return emailLabels[RocketChat.settings.get('Accounts_Default_User_Preferences_emailNotificationMode')];
+ },
showRoles() {
return RocketChat.settings.get('UI_DisplayRoles');
},
diff --git a/server/methods/saveUserPreferences.js b/server/methods/saveUserPreferences.js
index a694c51dbb12..2ae56398f5b3 100644
--- a/server/methods/saveUserPreferences.js
+++ b/server/methods/saveUserPreferences.js
@@ -45,6 +45,12 @@ Meteor.methods({
return false;
}
+ const {
+ desktopNotifications: oldDesktopNotifications,
+ mobileNotifications: oldMobileNotifications,
+ emailNotificationMode: oldEmailNotifications
+ } = (user.settings && user.settings.preferences) || {};
+
if (user.settings == null) {
RocketChat.models.Users.clearSettings(user._id);
}
@@ -57,14 +63,39 @@ Meteor.methods({
settings.mergeChannels = ['1', true].includes(settings.mergeChannels);
}
-
-
if (settings.roomsListExhibitionMode != null) {
settings.roomsListExhibitionMode = ['category', 'unread', 'activity'].includes(settings.roomsListExhibitionMode) ? settings.roomsListExhibitionMode : 'category';
}
RocketChat.models.Users.setPreferences(user._id, settings);
+ // propagate changed notification preferences
+ Meteor.defer(() => {
+ if (oldDesktopNotifications !== settings.desktopNotifications) {
+ if (settings.desktopNotifications === 'default') {
+ RocketChat.models.Subscriptions.clearDesktopNotificationUserPreferences(user._id);
+ } else {
+ RocketChat.models.Subscriptions.updateDesktopNotificationUserPreferences(user._id, settings.desktopNotifications);
+ }
+ }
+
+ if (oldMobileNotifications !== settings.mobileNotifications) {
+ if (settings.mobileNotifications === 'default') {
+ RocketChat.models.Subscriptions.clearMobileNotificationUserPreferences(user._id);
+ } else {
+ RocketChat.models.Subscriptions.updateMobileNotificationUserPreferences(user._id, settings.mobileNotifications);
+ }
+ }
+
+ if (oldEmailNotifications !== settings.emailNotificationMode) {
+ if (settings.emailNotificationMode === 'default') {
+ RocketChat.models.Subscriptions.clearEmailNotificationUserPreferences(user._id);
+ } else {
+ RocketChat.models.Subscriptions.updateEmailNotificationUserPreferences(user._id, settings.emailNotificationMode === 'disabled' ? 'nothing' : settings.emailNotificationMode);
+ }
+ }
+ });
+
return true;
}
});
diff --git a/server/startup/migrations/v116.js b/server/startup/migrations/v116.js
new file mode 100644
index 000000000000..3df0f661678e
--- /dev/null
+++ b/server/startup/migrations/v116.js
@@ -0,0 +1,88 @@
+RocketChat.Migrations.add({
+ version: 116,
+ up() {
+ RocketChat.models.Subscriptions.tryDropIndex({
+ unread: 1
+ });
+
+ // set pref origin to all existing preferences
+ RocketChat.models.Subscriptions.update({
+ desktopNotifications: { $exists: true }
+ }, {
+ $set: {
+ desktopPrefOrigin: 'subscription'
+ }
+ }, {
+ multi: true
+ });
+ RocketChat.models.Subscriptions.update({
+ mobilePushNotifications: { $exists: true }
+ }, {
+ $set: {
+ mobilePrefOrigin: 'subscription'
+ }
+ }, {
+ multi: true
+ });
+ RocketChat.models.Subscriptions.update({
+ emailNotifications: { $exists: true }
+ }, {
+ $set: {
+ emailPrefOrigin: 'subscription'
+ }
+ }, {
+ multi: true
+ });
+
+ // set user preferences on subscriptions
+ RocketChat.models.Users.find({
+ $or: [
+ { 'settings.preferences.desktopNotifications': { $exists: true } },
+ { 'settings.preferences.mobileNotifications': { $exists: true } },
+ { 'settings.preferences.emailNotificationMode': { $exists: true } }
+ ]
+ }).forEach(user => {
+ if (user.settings.preferences.desktopNotifications && user.settings.preferences.desktopNotifications !== 'default') {
+ RocketChat.models.Subscriptions.update({
+ 'u._id': user._id,
+ desktopPrefOrigin: { $exists: false }
+ }, {
+ $set: {
+ desktopNotifications: user.settings.preferences.desktopNotifications,
+ desktopPrefOrigin: 'user'
+ }
+ }, {
+ multi: true
+ });
+ }
+
+ if (user.settings.preferences.mobileNotifications && user.settings.preferences.mobileNotifications !== 'default') {
+ RocketChat.models.Subscriptions.update({
+ 'u._id': user._id,
+ mobilePrefOrigin: { $exists: false }
+ }, {
+ $set: {
+ mobileNotifications: user.settings.preferences.mobileNotifications,
+ mobilePrefOrigin: 'user'
+ }
+ }, {
+ multi: true
+ });
+ }
+
+ if (user.settings.preferences.emailNotificationMode && user.settings.preferences.emailNotificationMode !== 'default') {
+ RocketChat.models.Subscriptions.update({
+ 'u._id': user._id,
+ emailPrefOrigin: { $exists: false }
+ }, {
+ $set: {
+ emailNotifications: user.settings.preferences.emailNotificationMode === 'disabled' ? 'nothing' : user.settings.preferences.emailNotificationMode,
+ emailPrefOrigin: 'user'
+ }
+ }, {
+ multi: true
+ });
+ }
+ });
+ }
+});
diff --git a/tests/end-to-end/ui/11-admin.js b/tests/end-to-end/ui/11-admin.js
index d48070b8dc3e..95b0bcda7529 100644
--- a/tests/end-to-end/ui/11-admin.js
+++ b/tests/end-to-end/ui/11-admin.js
@@ -731,8 +731,8 @@ describe('[Administration]', () => {
admin.accountsEnableAutoAwayFalse.isVisible().should.be.true;
});
it('the enable auto away field value should be true', () => {
- admin.accountsEnableAutoAwayTrue.isSelected().should.be.false;
- admin.accountsEnableAutoAwayFalse.isSelected().should.be.true;
+ admin.accountsEnableAutoAwayTrue.isSelected().should.be.true;
+ admin.accountsEnableAutoAwayFalse.isSelected().should.be.false;
});
it('it should show the idle timeout limit field', () => {