diff --git a/app/api/server/v1/push.js b/app/api/server/v1/push.js index 9e3c8d59b325..937b62efc9d7 100644 --- a/app/api/server/v1/push.js +++ b/app/api/server/v1/push.js @@ -1,8 +1,12 @@ import { Meteor } from 'meteor/meteor'; import { Random } from 'meteor/random'; +import { Match, check } from 'meteor/check'; import { appTokensCollection } from '../../../push/server'; import { API } from '../api'; +import PushNotification from '../../../push-notifications/server/lib/PushNotification'; +import { canAccessRoom } from '../../../authorization/server/functions/canAccessRoom'; +import { Users, Messages, Rooms } from '../../../models/server'; API.v1.addRoute('push.token', { authRequired: true }, { post() { @@ -63,3 +67,35 @@ API.v1.addRoute('push.token', { authRequired: true }, { return API.v1.success(); }, }); + +API.v1.addRoute('push.get', { authRequired: true }, { + get() { + const params = this.requestParams(); + check(params, Match.ObjectIncluding({ + id: String, + })); + + const receiver = Users.findOneById(this.userId); + if (!receiver) { + throw new Error('error-user-not-found'); + } + + const message = Messages.findOneById(params.id); + if (!message) { + throw new Error('error-message-not-found'); + } + + const room = Rooms.findOneById(message.rid); + if (!room) { + throw new Error('error-room-not-found'); + } + + if (!canAccessRoom(room, receiver)) { + throw new Error('error-not-allowed'); + } + + const data = PushNotification.getNotificationForMessageId({ receiver, room, message }); + + return API.v1.success({ data }); + }, +}); diff --git a/app/api/server/v1/settings.js b/app/api/server/v1/settings.js index 284fea491be7..6cad33dca4be 100644 --- a/app/api/server/v1/settings.js +++ b/app/api/server/v1/settings.js @@ -6,6 +6,19 @@ import _ from 'underscore'; import { Settings } from '../../../models/server'; import { hasPermission } from '../../../authorization'; import { API } from '../api'; +import { SettingsEvents } from '../../../settings/server'; + +const fetchSettings = (query, sort, offset, count, fields) => { + const settings = Settings.find(query, { + sort: sort || { _id: 1 }, + skip: offset, + limit: count, + fields: Object.assign({ _id: 1, value: 1, enterprise: 1, invalidValue: 1, modules: 1 }, fields), + }).fetch(); + + SettingsEvents.emit('fetch-settings', settings); + return settings; +}; // settings endpoints API.v1.addRoute('settings.public', { authRequired: false }, { @@ -20,12 +33,7 @@ API.v1.addRoute('settings.public', { authRequired: false }, { ourQuery = Object.assign({}, query, ourQuery); - const settings = Settings.find(ourQuery, { - sort: sort || { _id: 1 }, - skip: offset, - limit: count, - fields: Object.assign({ _id: 1, value: 1 }, fields), - }).fetch(); + const settings = fetchSettings(ourQuery, sort, offset, count, fields); return API.v1.success({ settings, @@ -94,12 +102,7 @@ API.v1.addRoute('settings', { authRequired: true }, { ourQuery = Object.assign({}, query, ourQuery); - const settings = Settings.find(ourQuery, { - sort: sort || { _id: 1 }, - skip: offset, - limit: count, - fields: Object.assign({ _id: 1, value: 1 }, fields), - }).fetch(); + const settings = fetchSettings(ourQuery, sort, offset, count, fields); return API.v1.success({ settings, diff --git a/app/lib/server/startup/settings.js b/app/lib/server/startup/settings.js index 090d686083a6..5a9a6c0f5c18 100644 --- a/app/lib/server/startup/settings.js +++ b/app/lib/server/startup/settings.js @@ -1283,10 +1283,18 @@ settings.addGroup('Push', function() { type: 'boolean', public: true, }); - return this.add('Push_show_message', true, { + this.add('Push_show_message', true, { type: 'boolean', public: true, }); + this.add('Push_request_content_from_server', true, { + type: 'boolean', + enterprise: true, + invalidValue: false, + modules: [ + 'push-privacy', + ], + }); }); }); diff --git a/app/models/server/models/Settings.js b/app/models/server/models/Settings.js index a03c81a04100..2c766c24ca5b 100644 --- a/app/models/server/models/Settings.js +++ b/app/models/server/models/Settings.js @@ -58,7 +58,7 @@ export class Settings extends Base { filter._id = { $in: ids }; } - return this.find(filter, { fields: { _id: 1, value: 1, editor: 1 } }); + return this.find(filter, { fields: { _id: 1, value: 1, editor: 1, enterprise: 1, invalidValue: 1, modules: 1 } }); } findNotHiddenPublicUpdatedAfter(updatedAt) { @@ -70,7 +70,7 @@ export class Settings extends Base { }, }; - return this.find(filter, { fields: { _id: 1, value: 1, editor: 1 } }); + return this.find(filter, { fields: { _id: 1, value: 1, editor: 1, enterprise: 1, invalidValue: 1, modules: 1 } }); } findNotHiddenPrivate() { @@ -105,6 +105,10 @@ export class Settings extends Base { return this.find({ wizard: { $exists: true, $ne: null } }); } + findEnterpriseSettings() { + return this.find({ enterprise: true }); + } + // UPDATE updateValueById(_id, value) { const query = { diff --git a/app/push-notifications/server/lib/PushNotification.js b/app/push-notifications/server/lib/PushNotification.js index 76847b6f4c3c..dbbc587e69fe 100644 --- a/app/push-notifications/server/lib/PushNotification.js +++ b/app/push-notifications/server/lib/PushNotification.js @@ -3,7 +3,11 @@ import { Meteor } from 'meteor/meteor'; import { Push } from '../../../push/server'; import { settings } from '../../../settings/server'; import { metrics } from '../../../metrics/server'; +import { Users } from '../../../models/server'; import { RocketChatAssets } from '../../../assets/server'; +import { replaceMentionedUsernamesWithFullNames, parseMessageTextPerUser } from '../../../lib/server/functions/notifications'; +import { callbacks } from '../../../callbacks/server'; +import { getPushData } from '../../../lib/server/functions/notifications/mobile'; export class PushNotification { getNotificationId(roomId) { @@ -11,18 +15,7 @@ export class PushNotification { return this.hash(`${ serverId }|${ roomId }`); // hash } - hash(str) { - let hash = 0; - let i = str.length; - - while (i) { - hash = ((hash << 5) - hash) + str.charCodeAt(--i); - hash &= hash; // Convert to 32bit integer - } - return hash; - } - - send({ rid, uid: userId, mid: messageId, roomName, username, message, payload, badge = 1, category }) { + getNotificationConfig({ rid, uid: userId, mid: messageId, roomName, username, message, payload, badge = 1, category, idOnly = false }) { let title; if (roomName && roomName !== '') { title = `${ roomName }`; @@ -36,13 +29,14 @@ export class PushNotification { badge, sound: 'default', priority: 10, - title, - text: message, + title: idOnly ? '' : title, + text: idOnly ? '' : message, payload: { host: Meteor.absoluteUrl(), - rid, + ...idOnly || { rid }, messageId, - ...payload, + notificationType: idOnly ? 'message-id-only' : 'message', + ...idOnly || payload, }, userId, notId: this.getNotificationId(rid), @@ -58,9 +52,61 @@ export class PushNotification { }; } + return config; + } + + hash(str) { + let hash = 0; + let i = str.length; + + while (i) { + hash = ((hash << 5) - hash) + str.charCodeAt(--i); + hash &= hash; // Convert to 32bit integer + } + return hash; + } + + send({ rid, uid, mid, roomName, username, message, payload, badge = 1, category }) { + const idOnly = settings.get('Push_request_content_from_server'); + const config = this.getNotificationConfig({ rid, uid, mid, roomName, username, message, payload, badge, category, idOnly }); + metrics.notificationsSent.inc({ notification_type: 'mobile' }); return Push.send(config); } + + getNotificationForMessageId({ receiver, message, room }) { + const sender = Users.findOne(message.u._id, { fields: { username: 1, name: 1 } }); + if (!sender) { + throw new Error('Message sender not found'); + } + + let notificationMessage = callbacks.run('beforeSendMessageNotifications', message.msg); + if (message.mentions?.length > 0 && settings.get('UI_Use_Real_Name')) { + notificationMessage = replaceMentionedUsernamesWithFullNames(message.msg, message.mentions); + } + notificationMessage = parseMessageTextPerUser(notificationMessage, message, receiver); + + const pushData = Promise.await(getPushData({ + room, + message, + userId: receiver._id, + receiverUsername: receiver.username, + senderUsername: sender.username, + senderName: sender.name, + notificationMessage, + })); + + return { + message, + notification: this.getNotificationConfig({ + ...pushData, + rid: message.rid, + uid: message.u._id, + mid: message._id, + idOnly: false, + }), + }; + } } export default new PushNotification(); diff --git a/app/settings/client/lib/settings.ts b/app/settings/client/lib/settings.ts index 6a9597e9df48..c94664ae4d93 100644 --- a/app/settings/client/lib/settings.ts +++ b/app/settings/client/lib/settings.ts @@ -15,20 +15,18 @@ class Settings extends SettingsBase { return this.dict.get(_id); } + private _storeSettingValue(record: { _id: string; value: SettingValue }, initialLoad: boolean): void { + Meteor.settings[record._id] = record.value; + this.dict.set(record._id, record.value); + this.load(record._id, record.value, initialLoad); + } + init(): void { let initialLoad = true; this.collection.find().observe({ - added: (record: {_id: string; value: SettingValue}) => { - Meteor.settings[record._id] = record.value; - this.dict.set(record._id, record.value); - this.load(record._id, record.value, initialLoad); - }, - changed: (record: {_id: string; value: SettingValue}) => { - Meteor.settings[record._id] = record.value; - this.dict.set(record._id, record.value); - this.load(record._id, record.value, initialLoad); - }, - removed: (record: {_id: string}) => { + added: (record: { _id: string; value: SettingValue }) => this._storeSettingValue(record, initialLoad), + changed: (record: { _id: string; value: SettingValue }) => this._storeSettingValue(record, initialLoad), + removed: (record: { _id: string }) => { delete Meteor.settings[record._id]; this.dict.set(record._id, null); this.load(record._id, undefined, initialLoad); diff --git a/app/settings/server/functions/settings.ts b/app/settings/server/functions/settings.ts index 07eb26187b38..480c2f936832 100644 --- a/app/settings/server/functions/settings.ts +++ b/app/settings/server/functions/settings.ts @@ -1,3 +1,5 @@ +import { EventEmitter } from 'events'; + import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; @@ -15,6 +17,8 @@ if (process.env.SETTINGS_HIDDEN) { process.env.SETTINGS_HIDDEN.split(',').forEach((settingId) => hiddenSettings.add(settingId.trim())); } +export const SettingsEvents = new EventEmitter(); + const overrideSetting = (_id: string, value: SettingValue, options: ISettingAddOptions): SettingValue => { const envValue = process.env[_id]; if (envValue) { @@ -79,12 +83,19 @@ export interface ISettingAddOptions { multiline?: boolean; values?: Array; public?: boolean; + enterprise?: boolean; + modules?: Array; + invalidValue?: SettingValue; } - export interface ISettingSelectOption { key: string; i18nLabel: string; } +export interface ISettingRecord extends ISettingAddOptions { + _id: string; + env: boolean; + value: SettingValue; +} export interface ISettingAddGroupOptions { hidden?: boolean; @@ -94,6 +105,7 @@ export interface ISettingAddGroupOptions { i18nDescription?: string; } + interface IUpdateOperator { $set: ISettingAddOptions; $setOnInsert: ISettingAddOptions & { @@ -143,6 +155,13 @@ class Settings extends SettingsBase { options.hidden = options.hidden || false; options.blocked = options.blocked || false; options.secret = options.secret || false; + options.enterprise = options.enterprise || false; + + if (options.enterprise && !('invalidValue' in options)) { + console.error(`Enterprise setting ${ _id } is missing the invalidValue option`); + throw new Error(`Enterprise setting ${ _id } is missing the invalidValue option`); + } + if (options.group && options.sorter == null) { options.sorter = this._sorter[options.group]++; } @@ -329,33 +348,47 @@ class Settings extends SettingsBase { return SettingsModel.updateValueById(_id, undefined); } + /* + * Change a setting value on the Meteor.settings object + */ + storeSettingValue(record: ISettingRecord, initialLoad: boolean): void { + const newData = { + value: record.value, + }; + SettingsEvents.emit('store-setting-value', record, newData); + const { value } = newData; + + Meteor.settings[record._id] = value; + if (record.env === true) { + process.env[record._id] = String(value); + } + + this.load(record._id, value, initialLoad); + } + + /* + * Remove a setting value on the Meteor.settings object + */ + removeSettingValue(record: ISettingRecord, initialLoad: boolean): void { + SettingsEvents.emit('remove-setting-value', record); + + delete Meteor.settings[record._id]; + if (record.env === true) { + delete process.env[record._id]; + } + + this.load(record._id, undefined, initialLoad); + } + /* * Update a setting by id */ init(): void { this.initialLoad = true; SettingsModel.find().observe({ - added: (record: {_id: string; env: boolean; value: SettingValue}) => { - Meteor.settings[record._id] = record.value; - if (record.env === true) { - process.env[record._id] = String(record.value); - } - return this.load(record._id, record.value, this.initialLoad); - }, - changed: (record: {_id: string; env: boolean; value: SettingValue}) => { - Meteor.settings[record._id] = record.value; - if (record.env === true) { - process.env[record._id] = String(record.value); - } - return this.load(record._id, record.value, this.initialLoad); - }, - removed: (record: {_id: string; env: boolean}) => { - delete Meteor.settings[record._id]; - if (record.env === true) { - delete process.env[record._id]; - } - return this.load(record._id, undefined, this.initialLoad); - }, + added: (record: ISettingRecord) => this.storeSettingValue(record, this.initialLoad), + changed: (record: ISettingRecord) => this.storeSettingValue(record, this.initialLoad), + removed: (record: ISettingRecord) => this.removeSettingValue(record, this.initialLoad), }); this.initialLoad = false; this.afterInitialLoad.forEach((fn) => fn(Meteor.settings)); diff --git a/app/settings/server/index.ts b/app/settings/server/index.ts index edcce3a33231..7a4f6ebf00b4 100644 --- a/app/settings/server/index.ts +++ b/app/settings/server/index.ts @@ -1,6 +1,7 @@ -import { settings } from './functions/settings'; +import { settings, SettingsEvents } from './functions/settings'; import './observer'; export { settings, + SettingsEvents, }; diff --git a/ee/app/canned-responses/server/settings.js b/ee/app/canned-responses/server/settings.js index 0b204f9cd614..d9b35f1d5d2f 100644 --- a/ee/app/canned-responses/server/settings.js +++ b/ee/app/canned-responses/server/settings.js @@ -6,6 +6,11 @@ export const createSettings = () => { this.add('Canned_Responses_Enable', false, { type: 'boolean', public: true, + enterprise: true, + invalidValue: false, + modules: [ + 'canned-responses', + ], }); }); }); diff --git a/ee/app/ldap-enterprise/server/settings.js b/ee/app/ldap-enterprise/server/settings.js index 9d5d5738b247..a9925a5ec361 100644 --- a/ee/app/ldap-enterprise/server/settings.js +++ b/ee/app/ldap-enterprise/server/settings.js @@ -7,23 +7,48 @@ export const createSettings = () => { this.add('LDAP_Enable_LDAP_Roles_To_RC_Roles', false, { type: 'boolean', enableQuery: { _id: 'LDAP_Enable', value: true }, + enterprise: true, + invalidValue: false, + modules: [ + 'ldap-enterprise', + ], }); this.add('LDAP_Roles_To_Rocket_Chat_Roles', '{}', { type: 'code', enableQuery: { _id: 'LDAP_Enable_LDAP_Roles_To_RC_Roles', value: true }, + enterprise: true, + invalidValue: '{}', + modules: [ + 'ldap-enterprise', + ], }); this.add('LDAP_Validate_Roles_For_Each_Login', false, { type: 'boolean', enableQuery: { _id: 'LDAP_Enable_LDAP_Roles_To_RC_Roles', value: true }, + enterprise: true, + invalidValue: false, + modules: [ + 'ldap-enterprise', + ], }); this.add('LDAP_Default_Role_To_User', 'user', { type: 'select', values: Roles.find({ scope: 'Users' }).fetch().map((role) => ({ key: role._id, i18nLabel: role._id })), enableQuery: { _id: 'LDAP_Enable_LDAP_Roles_To_RC_Roles', value: true }, + enterprise: true, + invalidValue: 'user', + modules: [ + 'ldap-enterprise', + ], }); this.add('LDAP_Query_To_Get_User_Groups', '(&(ou=*)(uniqueMember=uid=#{username},dc=example,dc=com))', { type: 'string', enableQuery: { _id: 'LDAP_Enable_LDAP_Roles_To_RC_Roles', value: true }, + enterprise: true, + invalidValue: '(&(ou=*)(uniqueMember=uid=#{username},dc=example,dc=com))', + modules: [ + 'ldap-enterprise', + ], }); }); @@ -37,6 +62,11 @@ export const createSettings = () => { ], i18nDescription: 'LDAP_Sync_User_Active_State_Description', enableQuery: { _id: 'LDAP_Enable', value: true }, + enterprise: true, + invalidValue: 'none', + modules: [ + 'ldap-enterprise', + ], }); }); }); diff --git a/ee/app/license/server/bundles.ts b/ee/app/license/server/bundles.ts index a17796e7f4bd..102f0a62c418 100644 --- a/ee/app/license/server/bundles.ts +++ b/ee/app/license/server/bundles.ts @@ -9,6 +9,7 @@ const bundles: IBundle = { 'ldap-enterprise', 'livechat-enterprise', 'engagement-dashboard', + 'push-privacy', ], pro: [ ], diff --git a/ee/app/license/server/license.ts b/ee/app/license/server/license.ts index 63c8c8af9ebe..09027ae2fe98 100644 --- a/ee/app/license/server/license.ts +++ b/ee/app/license/server/license.ts @@ -183,6 +183,7 @@ class LicenseClass { return item; }); + EnterpriseLicenses.emit('validate'); this.showLicenses(); } @@ -271,6 +272,10 @@ export function onLicense(feature: string, cb: (...args: any[]) => void): void { EnterpriseLicenses.once(`valid:${ feature }`, cb); } +export function onValidateLicenses(cb: (...args: any[]) => void): void { + EnterpriseLicenses.on('validate', cb); +} + export interface IOverrideClassProperties { [key: string]: (...args: any[]) => any; } diff --git a/ee/app/livechat-enterprise/server/settings.js b/ee/app/livechat-enterprise/server/settings.js index e2e92660ca2e..ee5b5aa41a6f 100644 --- a/ee/app/livechat-enterprise/server/settings.js +++ b/ee/app/livechat-enterprise/server/settings.js @@ -7,6 +7,8 @@ export const createSettings = () => { group: 'Omnichannel', section: 'Routing', i18nLabel: 'Waiting_queue', + enterprise: true, + invalidValue: false, }); settings.add('Livechat_waiting_queue_message', '', { @@ -16,6 +18,11 @@ export const createSettings = () => { i18nLabel: 'Waiting_queue_message', i18nDescription: 'Waiting_queue_message_description', enableQuery: { _id: 'Livechat_waiting_queue', value: true }, + enterprise: true, + invalidValue: '', + modules: [ + 'livechat-enterprise', + ], }); settings.add('Livechat_maximum_chats_per_agent', 0, { @@ -25,6 +32,11 @@ export const createSettings = () => { i18nLabel: 'Max_number_of_chats_per_agent', i18nDescription: 'Max_number_of_chats_per_agent_description', enableQuery: { _id: 'Livechat_waiting_queue', value: true }, + enterprise: true, + invalidValue: 0, + modules: [ + 'livechat-enterprise', + ], }); settings.add('Livechat_number_most_recent_chats_estimate_wait_time', 100, { @@ -34,6 +46,11 @@ export const createSettings = () => { i18nLabel: 'Number_of_most_recent_chats_estimate_wait_time', i18nDescription: 'Number_of_most_recent_chats_estimate_wait_time_description', enableQuery: { _id: 'Livechat_waiting_queue', value: true }, + enterprise: true, + invalidValue: 100, + modules: [ + 'livechat-enterprise', + ], }); settings.add('Livechat_auto_close_abandoned_rooms', false, { @@ -41,6 +58,11 @@ export const createSettings = () => { group: 'Omnichannel', section: 'Sessions', i18nLabel: 'Enable_omnichannel_auto_close_abandoned_rooms', + enterprise: true, + invalidValue: false, + modules: [ + 'livechat-enterprise', + ], }); settings.add('Livechat_abandoned_rooms_closed_custom_message', '', { @@ -49,6 +71,11 @@ export const createSettings = () => { section: 'Sessions', i18nLabel: 'Livechat_abandoned_rooms_closed_custom_message', enableQuery: { _id: 'Livechat_auto_close_abandoned_rooms', value: true }, + enterprise: true, + invalidValue: '', + modules: [ + 'livechat-enterprise', + ], }); settings.add('Livechat_last_chatted_agent_routing', false, { @@ -56,6 +83,11 @@ export const createSettings = () => { group: 'Omnichannel', section: 'Routing', enableQuery: { _id: 'Livechat_Routing_Method', value: { $ne: 'Manual_Selection' } }, + enterprise: true, + invalidValue: false, + modules: [ + 'livechat-enterprise', + ], }); settings.addGroup('Omnichannel', function() { @@ -71,10 +103,14 @@ export const createSettings = () => { }], public: true, i18nLabel: 'Livechat_business_hour_type', + enterprise: true, + invalidValue: 'Single', + modules: [ + 'livechat-enterprise', + ], }); }); }); - Settings.addOptionValueById('Livechat_Routing_Method', { key: 'Load_Balancing', i18nLabel: 'Load_Balancing' }); }; diff --git a/ee/app/settings/server/index.js b/ee/app/settings/server/index.js new file mode 100644 index 000000000000..97097791afdc --- /dev/null +++ b/ee/app/settings/server/index.js @@ -0,0 +1 @@ +import './settings'; diff --git a/ee/app/settings/server/settings.ts b/ee/app/settings/server/settings.ts new file mode 100644 index 000000000000..560d057c5f3e --- /dev/null +++ b/ee/app/settings/server/settings.ts @@ -0,0 +1,70 @@ +import { Meteor } from 'meteor/meteor'; + +import { SettingsEvents, settings, ISettingRecord } from '../../../../app/settings/server/functions/settings'; +import { SettingValue } from '../../../../app/settings/lib/settings'; +import { isEnterprise, hasLicense, onValidateLicenses } from '../../license/server/license'; +import SettingsModel from '../../../../app/models/server/models/Settings'; + +function changeSettingValue(record: ISettingRecord): undefined | { value: SettingValue } { + if (!record.enterprise) { + return; + } + + if (!isEnterprise()) { + return { value: record.invalidValue }; + } + + if (!record.modules?.length) { + return; + } + + for (const moduleName of record.modules) { + if (!hasLicense(moduleName)) { + return { value: record.invalidValue }; + } + } +} + +SettingsEvents.on('store-setting-value', (record: ISettingRecord, newRecord: { value: SettingValue }) => { + const changedValue = changeSettingValue(record); + if (changedValue) { + newRecord.value = changedValue.value; + } +}); + +SettingsEvents.on('fetch-settings', (settings: Array): void => { + for (const setting of settings) { + const changedValue = changeSettingValue(setting); + if (changedValue) { + setting.value = changedValue.value; + } + } +}); + +type ISettingNotificationValue = { + _id: string; + value: SettingValue; + editor: string; + properties: string; + enterprise: boolean; +}; + +SettingsEvents.on('change-setting', (record: ISettingRecord, value: ISettingNotificationValue): void => { + const changedValue = changeSettingValue(record); + if (changedValue) { + value.value = changedValue.value; + } +}); + +function updateSettings(): void { + const enterpriseSettings = SettingsModel.findEnterpriseSettings(); + + enterpriseSettings.forEach((record: ISettingRecord) => settings.storeSettingValue(record, false)); +} + + +Meteor.startup(() => { + updateSettings(); + + onValidateLicenses(updateSettings); +}); diff --git a/ee/server/index.js b/ee/server/index.js index 0658aa609a40..b7aa74f4c07c 100644 --- a/ee/server/index.js +++ b/ee/server/index.js @@ -6,3 +6,4 @@ import '../app/canned-responses/server/index'; import '../app/engagement-dashboard/server/index'; import '../app/ldap-enterprise/server/index'; import '../app/livechat-enterprise/server/index'; +import '../app/settings/server/index'; diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 3d8df0d52a2c..f1451a0a0435 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -2870,6 +2870,7 @@ "Push_gcm_api_key": "GCM API Key", "Push_gcm_project_number": "GCM Project Number", "Push_production": "Production", + "Push_request_content_from_server": "Fetch full message content from the server on receipt", "Push_show_message": "Show Message in Notification", "Push_show_username_room": "Show Channel/Group/Username in Notification", "Push_test_push": "Test", diff --git a/server/publications/settings/index.js b/server/publications/settings/index.js index 81651a50bcbd..7b379059b50b 100644 --- a/server/publications/settings/index.js +++ b/server/publications/settings/index.js @@ -4,11 +4,14 @@ import { Settings } from '../../../app/models/server'; import { Notifications } from '../../../app/notifications/server'; import { hasPermission, hasAtLeastOnePermission } from '../../../app/authorization/server'; import { getSettingPermissionId } from '../../../app/authorization/lib'; +import { SettingsEvents } from '../../../app/settings/server/functions/settings'; Meteor.methods({ 'public-settings/get'(updatedAt) { if (updatedAt instanceof Date) { const records = Settings.findNotHiddenPublicUpdatedAfter(updatedAt).fetch(); + SettingsEvents.emit('fetch-settings', records); + return { update: records, remove: Settings.trashFindDeletedAfter(updatedAt, { @@ -24,7 +27,11 @@ Meteor.methods({ }).fetch(), }; } - return Settings.findNotHiddenPublic().fetch(); + + const publicSettings = Settings.findNotHiddenPublic().fetch(); + SettingsEvents.emit('fetch-settings', publicSettings); + + return publicSettings; }, 'private-settings/get'(updatedAfter) { const uid = Meteor.userId(); @@ -84,8 +91,11 @@ Settings.on('change', ({ clientAction, id, data, diff }) => { value: setting.value, editor: setting.editor, properties: setting.properties, + enterprise: setting.enterprise, }; + SettingsEvents.emit('change-setting', setting, value); + if (setting.public === true) { Notifications.notifyAllInThisInstance('public-settings-changed', clientAction, value); }