Fix link "go to message" on emails

- Gets the link based on room type using user's subscription data
- Do not loop over subscriptions for rooms without custom link
pull/3946/head
Diego Sampaio 10 years ago
parent 7875e0247e
commit c95be3ee7e
No known key found for this signature in database
GPG Key ID: E060152B30502562
  1. 114
      packages/rocketchat-lib/client/lib/roomTypes.coffee
  2. 62
      packages/rocketchat-lib/lib/roomTypesCommon.coffee
  3. 5
      packages/rocketchat-lib/package.js
  4. 23
      packages/rocketchat-lib/server/lib/roomTypes.coffee
  5. 27
      packages/rocketchat-lib/server/lib/sendEmailOnMessage.js
  6. 8
      packages/rocketchat-lib/server/models/Subscriptions.coffee
  7. 4
      packages/rocketchat-lib/startup/defaultRoomTypes.coffee
  8. 45
      packages/rocketchat-livechat/client/ui.js
  9. 1
      packages/rocketchat-livechat/package.js
  10. 44
      packages/rocketchat-livechat/roomType.js
  11. 2
      server/publications/room.coffee

@ -1,128 +1,60 @@
RocketChat.roomTypes = new class
roomTypesOrder = []
roomTypes = {}
mainOrder = 1
### Adds a room type to app
@param identifier An identifier to the room type. If a real room, MUST BE the same of `db.rocketchat_room.t` field, if not, can be null
@param order Order number of the type
@param config
template: template name to render on sideNav
icon: icon class
route:
name: route name
action: route action function
###
add = (identifier, order, config) ->
unless identifier?
identifier = Random.id()
if roomTypes[identifier]?
return false
if not order?
order = mainOrder + 10
mainOrder += 10
# @TODO validate config options
roomTypesOrder.push
identifier: identifier
order: order
roomTypes[identifier] = config
if config.route?.path? and config.route?.name? and config.route?.action?
FlowRouter.route config.route.path,
name: config.route.name
action: config.route.action
triggersExit: [roomExit]
###
@param roomType: room type (e.g.: c (for channels), d (for direct channels))
@param subData: the user's subscription data
###
getRouteLink = (roomType, subData) ->
unless roomTypes[roomType]?
return false
return FlowRouter.path roomTypes[roomType].route.name, roomTypes[roomType].route.link(subData)
checkCondition = (roomType) ->
RocketChat.roomTypes = new class roomTypesClient extends roomTypesCommon
checkCondition: (roomType) ->
return not roomType.condition? or roomType.condition()
getAllTypes = ->
getTypes: ->
orderedTypes = []
_.sortBy(roomTypesOrder, 'order').forEach (type) ->
orderedTypes.push roomTypes[type.identifier]
_.sortBy(@roomTypesOrder, 'order').forEach (type) =>
orderedTypes.push @roomTypes[type.identifier]
return orderedTypes
getIcon = (roomType) ->
return roomTypes[roomType]?.icon
getIcon: (roomType) ->
return @roomTypes[roomType]?.icon
getRoomName = (roomType, roomData) ->
return roomTypes[roomType]?.roomName roomData
getRoomName: (roomType, roomData) ->
return @roomTypes[roomType]?.roomName roomData
getIdentifiers = (except) ->
getIdentifiers: (except) ->
except = [].concat except
list = _.reject roomTypesOrder, (t) -> return except.indexOf(t.identifier) isnt -1
list = _.reject @roomTypesOrder, (t) -> return except.indexOf(t.identifier) isnt -1
return _.map list, (t) -> return t.identifier
findRoom = (roomType, identifier, user) ->
return roomTypes[roomType]?.findRoom identifier, user
findRoom: (roomType, identifier, user) ->
return @roomTypes[roomType]?.findRoom identifier, user
canSendMessage = (roomId) ->
canSendMessage: (roomId) ->
return ChatSubscription.find({ rid: roomId }).count() > 0
verifyCanSendMessage = (roomId) ->
verifyCanSendMessage: (roomId) ->
room = ChatRoom.findOne({ _id: roomId }, { fields: { t: 1 } })
return if not room?.t?
roomType = room.t
return roomTypes[roomType]?.canSendMessage roomId if roomTypes[roomType]?.canSendMessage?
return @roomTypes[roomType]?.canSendMessage roomId if @roomTypes[roomType]?.canSendMessage?
return canSendMessage roomId
return @canSendMessage roomId
verifyShowJoinLink = (roomId) ->
verifyShowJoinLink: (roomId) ->
room = ChatRoom.findOne({ _id: roomId }, { fields: { t: 1 } })
return if not room?.t?
roomType = room.t
if not roomTypes[roomType]?.showJoinLink?
if not @roomTypes[roomType]?.showJoinLink?
return false
return roomTypes[roomType].showJoinLink roomId
return @roomTypes[roomType].showJoinLink roomId
getNotSubscribedTpl = (roomId) ->
getNotSubscribedTpl: (roomId) ->
room = ChatRoom.findOne({ _id: roomId }, { fields: { t: 1 } })
return if not room?.t?
roomType = room.t
if not roomTypes[roomType]?.notSubscribedTpl?
if not @roomTypes[roomType]?.notSubscribedTpl?
return false
return roomTypes[roomType].notSubscribedTpl
# addType: addType
getTypes: getAllTypes
getIdentifiers: getIdentifiers
findRoom: findRoom
# setIcon: setIcon
getIcon: getIcon
getRoomName: getRoomName
# setRoute: setRoute
getRouteLink: getRouteLink
checkCondition: checkCondition
verifyCanSendMessage: verifyCanSendMessage
verifyShowJoinLink: verifyShowJoinLink
getNotSubscribedTpl: getNotSubscribedTpl
add: add
return @roomTypes[roomType].notSubscribedTpl

@ -0,0 +1,62 @@
class @roomTypesCommon
roomTypes: {}
roomTypesOrder: []
mainOrder: 1
### Adds a room type to app
@param identifier An identifier to the room type. If a real room, MUST BE the same of `db.rocketchat_room.t` field, if not, can be null
@param order Order number of the type
@param config
template: template name to render on sideNav
icon: icon class
route:
name: route name
action: route action function
###
add: (identifier, order, config) ->
unless identifier?
identifier = Random.id()
if @roomTypes[identifier]?
return false
if not order?
order = @mainOrder + 10
@mainOrder += 10
# @TODO validate config options
@roomTypesOrder.push
identifier: identifier
order: order
@roomTypes[identifier] = config
if config.route?.path? and config.route?.name? and config.route?.action?
routeConfig =
name: config.route.name
action: config.route.action
if Meteor.isClient
routeConfig.triggersExit = [ roomExit ]
FlowRouter.route config.route.path, routeConfig
hasCustomLink: (roomType) ->
return @roomTypes[roomType]?.route?.link?
###
@param roomType: room type (e.g.: c (for channels), d (for direct channels))
@param subData: the user's subscription data
###
getRouteLink: (roomType, subData) ->
unless @roomTypes[roomType]?
return false
routeData = {}
if @roomTypes[roomType]?.route?.link?
routeData = @roomTypes[roomType].route.link(subData)
else if subData?.name?
routeData = { name: subData.name }
return FlowRouter.path @roomTypes[roomType].route.name, routeData

@ -34,7 +34,7 @@ Package.onUse(function(api) {
api.use('rocketchat:logger');
api.use('templating', 'client');
api.use('kadira:flow-router', 'client');
api.use('kadira:flow-router');
api.addFiles('lib/core.coffee');
@ -48,6 +48,7 @@ Package.onUse(function(api) {
api.addFiles('lib/fileUploadRestrictions.js');
api.addFiles('lib/placeholders.js');
api.addFiles('lib/promises.coffee');
api.addFiles('lib/roomTypesCommon.coffee');
api.addFiles('lib/slashCommand.coffee');
api.addFiles('lib/Message.coffee');
api.addFiles('lib/MessageTypes.coffee');
@ -138,6 +139,8 @@ Package.onUse(function(api) {
api.addFiles('client/models/_Base.coffee', 'client');
api.addFiles('client/models/Uploads.coffee', 'client');
api.addFiles('startup/defaultRoomTypes.coffee');
// VERSION
api.addFiles('rocketchat.info');

@ -1,26 +1,23 @@
RocketChat.roomTypes = new class
roomTypes = {}
RocketChat.roomTypes = new class roomTypesServer extends roomTypesCommon
### add a publish for a room type
@param roomType: room type (e.g.: c (for channels), d (for direct channels))
@param callback: function that will return the publish's data
###
setPublish = (roomType, callback) ->
if roomTypes[roomType]?.publish?
setPublish: (roomType, callback) ->
if @roomTypes[roomType]?.publish?
throw new Meteor.Error 'route-publish-exists', 'Publish for the given type already exists'
unless roomTypes[roomType]?
roomTypes[roomType] = {}
unless @roomTypes[roomType]?
@roomTypes[roomType] = {}
roomTypes[roomType].publish = callback
@roomTypes[roomType].publish = callback
### run the publish for a room type
@param scope: Meteor publish scope
@param roomType: room type (e.g.: c (for channels), d (for direct channels))
@param identifier: identifier of the room
###
runPublish = (roomType, identifier) ->
return unless roomTypes[roomType].publish?
return roomTypes[roomType].publish.call this, identifier
runPublish: (scope, roomType, identifier) ->
return unless @roomTypes[roomType]?.publish?
return @roomTypes[roomType].publish.call scope, identifier
setPublish: setPublish
runPublish: runPublish

@ -27,8 +27,9 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
});
}
var getMessageLink = () => {
var path = directMessage ? `direct/${ room.username }` : `channel/${ room.name }`;
var getMessageLink = (room, sub) => {
var roomPath = RocketChat.roomTypes.getRouteLink(room.t, sub);
var path = Meteor.absoluteUrl(roomPath ? roomPath.replace(/^\//, '') : '');
var style = [
'color: #fff;',
'padding: .5em;',
@ -41,24 +42,23 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
'margin-bottom: 8px;'
].join(' ');
var message = TAPi18n.__('Offline_Link_Message');
return `<a style="${ style }" href="${ process.env.ROOT_URL }${ path }">${ message }</a>`;
return `<a style="${ style }" href="${ path }">${ message }</a>`;
};
var divisorMessage = '<hr style="margin: 20px auto; border: none; border-bottom: 1px solid #dddddd;">';
message.html = s.escapeHTML(message.msg) + divisorMessage + getMessageLink();
var 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');
message.html = message.html.replace(token.token, token.text);
messageHTML = messageHTML.replace(token.token, token.text);
});
}
var header = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Header') || '');
var footer = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Footer') || '');
message.html = header + message.html.replace(/\n/gm, '<br/>') + footer;
messageHTML = messageHTML.replace(/\n/gm, '<br/>');
RocketChat.models.Subscriptions.findWithSendEmailByRoomId(room._id).forEach((sub) => {
switch (sub.emailNotifications) {
@ -80,6 +80,17 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
var userIdsToSendEmail = Object.keys(usersToSendEmail);
var defaultLink;
var 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) {
var usersOfMention = RocketChat.models.Users.getUsersToSendOfflineEmail(userIdsToSendEmail).fetch();
@ -102,7 +113,7 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
to: email.address,
from: RocketChat.settings.get('From_Email'),
subject: `[${ siteName }] ${ emailSubject }`,
html: message.html
html: header + messageHTML + divisorMessage + (linkByUser[user._id] || defaultLink) + footer
};
Email.send(email);

@ -78,6 +78,14 @@ RocketChat.models.Subscriptions = new class extends RocketChat.models._Base
return @find(query, options)?.fetch?()?[0]?.ls
findByRoomIdAndUserIds: (roomId, userIds) ->
query =
rid: roomId
'u._id':
$in: userIds
return @find query
# UPDATE
archiveByRoomIdAndUserId: (roomId, userId) ->
query =

@ -11,8 +11,6 @@ RocketChat.roomTypes.add 'c', 10,
action: (params, queryParams) ->
openRoom 'c', params.name
RocketChat.TabBar.showGroup 'channel'
link: (sub) ->
return { name: sub.name }
findRoom: (identifier) ->
query =
t: 'c'
@ -56,8 +54,6 @@ RocketChat.roomTypes.add 'p', 30,
action: (params, queryParams) ->
openRoom 'p', params.name
RocketChat.TabBar.showGroup 'privategroup'
link: (sub) ->
return { name: sub.name }
findRoom: (identifier) ->
query =
t: 'p'

@ -1,48 +1,3 @@
/* globals openRoom */
RocketChat.roomTypes.add('l', 5, {
template: 'livechat',
icon: 'icon-chat-empty',
route: {
name: 'live',
path: '/live/:code(\\d+)',
action(params/*, queryParams*/) {
openRoom('l', params.code);
RocketChat.TabBar.showGroup('livechat', 'search');
},
link(sub) {
return {
code: sub.code
};
}
},
findRoom(identifier) {
return ChatRoom.findOne({ code: parseInt(identifier) });
},
roomName(roomData) {
if (!roomData.name) {
return roomData.label;
} else {
return roomData.name;
}
},
condition() {
return RocketChat.settings.get('Livechat_enabled') && RocketChat.authz.hasAllPermission('view-l-room');
},
canSendMessage(roomId) {
let room = ChatRoom.findOne({ _id: roomId }, { fields: { open: 1 } });
return room && room.open === true;
},
notSubscribedTpl: {
template: 'livechatNotSubscribed'
}
});
AccountBox.addItem({
name: 'Livechat',
icon: 'icon-chat-empty',

@ -38,6 +38,7 @@ Package.onUse(function(api) {
api.addFiles('livechat.js', 'server');
api.addFiles('server/startup.js', 'server');
api.addFiles('permissions.js', 'server');
api.addFiles('roomType.js');
api.addFiles('config.js', 'server');

@ -0,0 +1,44 @@
/* globals openRoom */
RocketChat.roomTypes.add('l', 5, {
template: 'livechat',
icon: 'icon-chat-empty',
route: {
name: 'live',
path: '/live/:code(\\d+)',
action(params/*, queryParams*/) {
openRoom('l', params.code);
RocketChat.TabBar.showGroup('livechat', 'search');
},
link(sub) {
return {
code: sub.code
};
}
},
findRoom(identifier) {
return ChatRoom.findOne({ code: parseInt(identifier) });
},
roomName(roomData) {
if (!roomData.name) {
return roomData.label;
} else {
return roomData.name;
}
},
condition() {
return RocketChat.settings.get('Livechat_enabled') && RocketChat.authz.hasAllPermission('view-l-room');
},
canSendMessage(roomId) {
let room = ChatRoom.findOne({ _id: roomId }, { fields: { open: 1 } });
return room && room.open === true;
},
notSubscribedTpl: {
template: 'livechatNotSubscribed'
}
});

@ -8,4 +8,4 @@ Meteor.publish 'room', (typeName) ->
type = typeName.substr(0, 1)
name = typeName.substr(1)
return RocketChat.roomTypes.runPublish.call(this, type, name)
return RocketChat.roomTypes.runPublish(this, type, name)

Loading…
Cancel
Save