finished conversion rocketchat-lib

pull/6735/head
Guilherme Gazzo 8 years ago
parent e6ba3d84bc
commit 1c2e35bd93
  1. 25
      packages/rocketchat-lib/package.js
  2. 119
      packages/rocketchat-lib/server/functions/Notifications.coffee
  3. 132
      packages/rocketchat-lib/server/functions/Notifications.js
  4. 20
      packages/rocketchat-lib/server/functions/checkUsernameAvailability.js
  5. 50
      packages/rocketchat-lib/server/functions/sendMessage.coffee
  6. 59
      packages/rocketchat-lib/server/functions/sendMessage.js
  7. 77
      packages/rocketchat-lib/server/functions/setUsername.coffee
  8. 85
      packages/rocketchat-lib/server/functions/setUsername.js
  9. 248
      packages/rocketchat-lib/server/functions/settings.coffee
  10. 283
      packages/rocketchat-lib/server/functions/settings.js
  11. 64
      packages/rocketchat-lib/server/methods/sendMessage.coffee
  12. 81
      packages/rocketchat-lib/server/methods/sendMessage.js
  13. 580
      packages/rocketchat-lib/server/models/Messages.js
  14. 760
      packages/rocketchat-lib/server/models/Rooms.js
  15. 178
      packages/rocketchat-lib/server/models/Settings.js
  16. 570
      packages/rocketchat-lib/server/models/Subscriptions.js
  17. 91
      packages/rocketchat-lib/server/models/Uploads.js
  18. 538
      packages/rocketchat-lib/server/models/Users.js

@ -21,7 +21,6 @@ Package.onUse(function(api) {
api.use('reactive-var');
api.use('reactive-dict');
api.use('accounts-base');
api.use('coffeescript');
api.use('ecmascript');
api.use('random');
api.use('check');
@ -72,7 +71,7 @@ Package.onUse(function(api) {
api.addFiles('server/functions/addUserToDefaultChannels.js', 'server');
api.addFiles('server/functions/addUserToRoom.js', 'server');
api.addFiles('server/functions/archiveRoom.js', 'server');
api.addFiles('server/functions/checkUsernameAvailability.coffee', 'server');
api.addFiles('server/functions/checkUsernameAvailability.js', 'server');
api.addFiles('server/functions/checkEmailAvailability.js', 'server');
api.addFiles('server/functions/createRoom.js', 'server');
api.addFiles('server/functions/deleteMessage.js', 'server');
@ -82,15 +81,15 @@ Package.onUse(function(api) {
api.addFiles('server/functions/removeUserFromRoom.js', 'server');
api.addFiles('server/functions/saveUser.js', 'server');
api.addFiles('server/functions/saveCustomFields.js', 'server');
api.addFiles('server/functions/sendMessage.coffee', 'server');
api.addFiles('server/functions/settings.coffee', 'server');
api.addFiles('server/functions/sendMessage.js', 'server');
api.addFiles('server/functions/settings.js', 'server');
api.addFiles('server/functions/setUserAvatar.js', 'server');
api.addFiles('server/functions/setUsername.coffee', 'server');
api.addFiles('server/functions/setUsername.js', 'server');
api.addFiles('server/functions/setRealName.js', 'server');
api.addFiles('server/functions/setEmail.js', 'server');
api.addFiles('server/functions/unarchiveRoom.js', 'server');
api.addFiles('server/functions/updateMessage.js', 'server');
api.addFiles('server/functions/Notifications.coffee', 'server');
api.addFiles('server/functions/Notifications.js', 'server');
// SERVER LIB
api.addFiles('server/lib/configLogger.js', 'server');
@ -104,13 +103,13 @@ Package.onUse(function(api) {
// SERVER MODELS
api.addFiles('server/models/_Base.js', 'server');
api.addFiles('server/models/Messages.coffee', 'server');
api.addFiles('server/models/Messages.js', 'server');
api.addFiles('server/models/Reports.js', 'server');
api.addFiles('server/models/Rooms.coffee', 'server');
api.addFiles('server/models/Settings.coffee', 'server');
api.addFiles('server/models/Subscriptions.coffee', 'server');
api.addFiles('server/models/Uploads.coffee', 'server');
api.addFiles('server/models/Users.coffee', 'server');
api.addFiles('server/models/Rooms.js', 'server');
api.addFiles('server/models/Settings.js', 'server');
api.addFiles('server/models/Subscriptions.js', 'server');
api.addFiles('server/models/Uploads.js', 'server');
api.addFiles('server/models/Users.js', 'server');
api.addFiles('server/oauth/oauth.js', 'server');
api.addFiles('server/oauth/google.js', 'server');
@ -153,7 +152,7 @@ Package.onUse(function(api) {
api.addFiles('server/methods/robotMethods.js', 'server');
api.addFiles('server/methods/saveSetting.js', 'server');
api.addFiles('server/methods/sendInvitationEmail.js', 'server');
api.addFiles('server/methods/sendMessage.coffee', 'server');
api.addFiles('server/methods/sendMessage.js', 'server');
api.addFiles('server/methods/sendSMTPTestEmail.js', 'server');
api.addFiles('server/methods/setAdminStatus.js', 'server');
api.addFiles('server/methods/setRealName.js', 'server');

@ -1,119 +0,0 @@
RocketChat.Notifications = new class
constructor: ->
self = @
@debug = false
@streamAll = new Meteor.Streamer 'notify-all'
@streamLogged = new Meteor.Streamer 'notify-logged'
@streamRoom = new Meteor.Streamer 'notify-room'
@streamRoomUsers = new Meteor.Streamer 'notify-room-users'
@streamUser = new Meteor.Streamer 'notify-user'
@streamAll.allowWrite('none')
@streamLogged.allowWrite('none')
@streamRoom.allowWrite('none')
@streamRoomUsers.allowWrite (eventName, args...) ->
[roomId, e] = eventName.split('/')
user = Meteor.users.findOne @userId, {fields: {username: 1}}
if RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(roomId, @userId)?
subscriptions = RocketChat.models.Subscriptions.findByRoomIdAndNotUserId(roomId, @userId).fetch()
for subscription in subscriptions
RocketChat.Notifications.notifyUser(subscription.u._id, e, args...)
return false
@streamUser.allowWrite('logged')
@streamAll.allowRead('all')
@streamLogged.allowRead('logged')
@streamRoom.allowRead (eventName) ->
if not @userId? then return false
roomId = eventName.split('/')[0]
user = Meteor.users.findOne @userId, {fields: {username: 1}}
room = RocketChat.models.Rooms.findOneById(roomId)
if room.t is 'l' and room.v._id is user._id
return true
return room.usernames.indexOf(user.username) > -1
@streamRoomUsers.allowRead('none');
@streamUser.allowRead (eventName) ->
userId = eventName.split('/')[0]
return @userId? and @userId is userId
notifyAll: (eventName, args...) ->
console.log 'notifyAll', arguments if @debug is true
args.unshift eventName
@streamAll.emit.apply @streamAll, args
notifyLogged: (eventName, args...) ->
console.log 'notifyLogged', arguments if @debug is true
args.unshift eventName
@streamLogged.emit.apply @streamLogged, args
notifyRoom: (room, eventName, args...) ->
console.log 'notifyRoom', arguments if @debug is true
args.unshift "#{room}/#{eventName}"
@streamRoom.emit.apply @streamRoom, args
notifyUser: (userId, eventName, args...) ->
console.log 'notifyUser', arguments if @debug is true
args.unshift "#{userId}/#{eventName}"
@streamUser.emit.apply @streamUser, args
notifyAllInThisInstance: (eventName, args...) ->
console.log 'notifyAll', arguments if @debug is true
args.unshift eventName
@streamAll.emitWithoutBroadcast.apply @streamAll, args
notifyLoggedInThisInstance: (eventName, args...) ->
console.log 'notifyLogged', arguments if @debug is true
args.unshift eventName
@streamLogged.emitWithoutBroadcast.apply @streamLogged, args
notifyRoomInThisInstance: (room, eventName, args...) ->
console.log 'notifyRoomAndBroadcast', arguments if @debug is true
args.unshift "#{room}/#{eventName}"
@streamRoom.emitWithoutBroadcast.apply @streamRoom, args
notifyUserInThisInstance: (userId, eventName, args...) ->
console.log 'notifyUserAndBroadcast', arguments if @debug is true
args.unshift "#{userId}/#{eventName}"
@streamUser.emitWithoutBroadcast.apply @streamUser, args
## Permissions for client
# Enable emit for event typing for rooms and add username to event data
func = (eventName, username) ->
[room, e] = eventName.split('/')
if e is 'webrtc'
return true
if e is 'typing'
user = Meteor.users.findOne(@userId, {fields: {username: 1}})
if user?.username is username
return true
return false
RocketChat.Notifications.streamRoom.allowWrite func

@ -0,0 +1,132 @@
RocketChat.Notifications = new class {
constructor() {
this.debug = false;
this.streamAll = new Meteor.Streamer('notify-all');
this.streamLogged = new Meteor.Streamer('notify-logged');
this.streamRoom = new Meteor.Streamer('notify-room');
this.streamRoomUsers = new Meteor.Streamer('notify-room-users');
this.streamUser = new Meteor.Streamer('notify-user');
this.streamAll.allowWrite('none');
this.streamLogged.allowWrite('none');
this.streamRoom.allowWrite('none');
this.streamRoomUsers.allowWrite(function(eventName, ...args) {
const [roomId, e] = eventName.split('/');
// const user = Meteor.users.findOne(this.userId, {
// fields: {
// username: 1
// }
// });
if (RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(roomId, this.userId) != null) {
const subscriptions = RocketChat.models.Subscriptions.findByRoomIdAndNotUserId(roomId, this.userId).fetch();
subscriptions.forEach(subscription => RocketChat.Notifications.notifyUser(subscription.u._id, e, ...args));
}
return false;
});
this.streamUser.allowWrite('logged');
this.streamAll.allowRead('all');
this.streamLogged.allowRead('logged');
this.streamRoom.allowRead(function(eventName) {
if (this.userId == null) {
return false;
}
const [roomId] = eventName.split('/');
const user = Meteor.users.findOne(this.userId, {
fields: {
username: 1
}
});
const room = RocketChat.models.Rooms.findOneById(roomId);
if (room.t === 'l' && room.v._id === user._id) {
return true;
}
return room.usernames.indexOf(user.username) > -1;
});
this.streamRoomUsers.allowRead('none');
this.streamUser.allowRead(function(eventName) {
const [userId] = eventName.split('/');
return (this.userId != null) && this.userId === userId;
});
}
notifyAll(eventName, ...args) {
if (this.debug === true) {
console.log('notifyAll', arguments);
}
args.unshift(eventName);
return this.streamAll.emit.apply(this.streamAll, args);
}
notifyLogged(eventName, ...args) {
if (this.debug === true) {
console.log('notifyLogged', arguments);
}
args.unshift(eventName);
return this.streamLogged.emit.apply(this.streamLogged, args);
}
notifyRoom(room, eventName, ...args) {
if (this.debug === true) {
console.log('notifyRoom', arguments);
}
args.unshift(`${ room }/${ eventName }`);
return this.streamRoom.emit.apply(this.streamRoom, args);
}
notifyUser(userId, eventName, ...args) {
if (this.debug === true) {
console.log('notifyUser', arguments);
}
args.unshift(`${ userId }/${ eventName }`);
return this.streamUser.emit.apply(this.streamUser, args);
}
notifyAllInThisInstance(eventName, ...args) {
if (this.debug === true) {
console.log('notifyAll', arguments);
}
args.unshift(eventName);
return this.streamAll.emitWithoutBroadcast.apply(this.streamAll, args);
}
notifyLoggedInThisInstance(eventName, ...args) {
if (this.debug === true) {
console.log('notifyLogged', arguments);
}
args.unshift(eventName);
return this.streamLogged.emitWithoutBroadcast.apply(this.streamLogged, args);
}
notifyRoomInThisInstance(room, eventName, ...args) {
if (this.debug === true) {
console.log('notifyRoomAndBroadcast', arguments);
}
args.unshift(`${ room }/${ eventName }`);
return this.streamRoom.emitWithoutBroadcast.apply(this.streamRoom, args);
}
notifyUserInThisInstance(userId, eventName, ...args) {
if (this.debug === true) {
console.log('notifyUserAndBroadcast', arguments);
}
args.unshift(`${ userId }/${ eventName }`);
return this.streamUser.emitWithoutBroadcast.apply(this.streamUser, args);
}
};
RocketChat.Notifications.streamRoom.allowWrite(function(eventName, username) {
const [, e] = eventName.split('/');
if (e === 'webrtc') {
return true;
}
if (e === 'typing') {
const user = Meteor.users.findOne(this.userId, {
fields: {
username: 1
}
});
if (user != null && user.username === username) {
return true;
}
}
return false;
});

@ -0,0 +1,20 @@
RocketChat.checkUsernameAvailability = function(username) {
return RocketChat.settings.get('Accounts_BlockedUsernameList', function(key, value) {
const usernameBlackList = _.map(value.split(','), function(username) {
return username.trim();
});
if (usernameBlackList.length !== 0) {
if (usernameBlackList.every(restrictedUsername => {
const regex = new RegExp(`^${ s.escapeRegExp(restrictedUsername) }$`, 'i');
return !regex.test(s.trim(s.escapeRegExp(username)));
})) {
return !Meteor.users.findOne({
username: {
$regex: new RegExp(`^${ s.trim(s.escapeRegExp(username)) }$`, 'i')
}
});
}
return false;
}
});
};

@ -1,50 +0,0 @@
RocketChat.sendMessage = (user, message, room, upsert = false) ->
if not user or not message or not room._id
return false
unless message.ts?
message.ts = new Date()
message.u = _.pick user, ['_id','username']
if not Match.test(message.msg, String)
message.msg = ''
message.rid = room._id
if not room.usernames? || room.usernames.length is 0
updated_room = RocketChat.models.Rooms.findOneById(room._id)
if updated_room?
room = updated_room
else
room.usernames = []
if message.parseUrls isnt false
if urls = message.msg.match /([A-Za-z]{3,9}):\/\/([-;:&=\+\$,\w]+@{1})?([-A-Za-z0-9\.]+)+:?(\d+)?((\/[-\+=!:~%\/\.@\,\w]*)?\??([-\+=&!:;%@\/\.\,\w]+)?(?:#([^\s\)]+))?)?/g
message.urls = urls.map (url) -> url: url
message = RocketChat.callbacks.run 'beforeSaveMessage', message
# Avoid saving sandstormSessionId to the database
sandstormSessionId = null
if message.sandstormSessionId
sandstormSessionId = message.sandstormSessionId
delete message.sandstormSessionId
if message._id? and upsert
_id = message._id
delete message._id
RocketChat.models.Messages.upsert {_id: _id, 'u._id': message.u._id}, message
message._id = _id
else
message._id = RocketChat.models.Messages.insert message
###
Defer other updates as their return is not interesting to the user
###
Meteor.defer ->
# Execute all callbacks
message.sandstormSessionId = sandstormSessionId
RocketChat.callbacks.run 'afterSaveMessage', message, room
return message

@ -0,0 +1,59 @@
RocketChat.sendMessage = function(user, message, room, upsert = false) {
if (!user || !message || !room._id) {
return false;
}
if (message.ts == null) {
message.ts = new Date();
}
message.u = _.pick(user, ['_id', 'username', 'name']);
if (!Match.test(message.msg, String)) {
message.msg = '';
}
message.rid = room._id;
if (room.usernames || room.usernames.length === 0) {
const updated_room = RocketChat.models.Rooms.findOneById(room._id);
if (updated_room != null) {
room = updated_room;
} else {
room.usernames = [];
}
}
if (message.parseUrls !== false) {
const urls = message.msg.match(/([A-Za-z]{3,9}):\/\/([-;:&=\+\$,\w]+@{1})?([-A-Za-z0-9\.]+)+:?(\d+)?((\/[-\+=!:~%\/\.@\,\w]*)?\??([-\+=&!:;%@\/\.\,\w]+)?(?:#([^\s\)]+))?)?/g);
if (urls) {
message.urls = urls.map(function(url) {
return {
url
};
});
}
}
message = RocketChat.callbacks.run('beforeSaveMessage', message);
// Avoid saving sandstormSessionId to the database
let sandstormSessionId = null;
if (message.sandstormSessionId) {
sandstormSessionId = message.sandstormSessionId;
delete message.sandstormSessionId;
}
if (message._id && upsert) {
const _id = message._id;
delete message._id;
RocketChat.models.Messages.upsert({
_id,
'u._id': message.u._id
}, message);
message._id = _id;
} else {
message._id = RocketChat.models.Messages.insert(message);
}
/*
Defer other updates as their return is not interesting to the user
*/
Meteor.defer(() => {
// Execute all callbacks
message.sandstormSessionId = sandstormSessionId;
return RocketChat.callbacks.run('afterSaveMessage', message, room);
});
return message;
};

@ -1,77 +0,0 @@
RocketChat._setUsername = (userId, username) ->
username = s.trim username
if not userId or not username
return false
try
nameValidation = new RegExp '^' + RocketChat.settings.get('UTF8_Names_Validation') + '$'
catch
nameValidation = new RegExp '^[0-9a-zA-Z-_.]+$'
if not nameValidation.test username
return false
user = RocketChat.models.Users.findOneById userId
# User already has desired username, return
if user.username is username
return user
previousUsername = user.username
# Check username availability or if the user already owns a different casing of the name
if ( !previousUsername or !(username.toLowerCase() == previousUsername.toLowerCase()))
unless RocketChat.checkUsernameAvailability username
return false
# If first time setting username, send Enrollment Email
try
if not previousUsername and user.emails?.length > 0 and RocketChat.settings.get 'Accounts_Enrollment_Email'
Accounts.sendEnrollmentEmail(user._id)
catch error
user.username = username
# If first time setting username, check if should set default avatar
if not previousUsername and RocketChat.settings.get('Accounts_SetDefaultAvatar') is true
avatarSuggestions = getAvatarSuggestionForUser user
for service, avatarData of avatarSuggestions
if service isnt 'gravatar'
RocketChat.setUserAvatar(user, avatarData.blob, avatarData.contentType, service)
gravatar = null
break
else
gravatar = avatarData
if gravatar?
RocketChat.setUserAvatar(user, gravatar.blob, gravatar.contentType, 'gravatar')
# Username is available; if coming from old username, update all references
if previousUsername
RocketChat.models.Messages.updateAllUsernamesByUserId user._id, username
RocketChat.models.Messages.updateUsernameOfEditByUserId user._id, username
RocketChat.models.Messages.findByMention(previousUsername).forEach (msg) ->
updatedMsg = msg.msg.replace(new RegExp("@#{previousUsername}", "ig"), "@#{username}")
RocketChat.models.Messages.updateUsernameAndMessageOfMentionByIdAndOldUsername msg._id, previousUsername, username, updatedMsg
RocketChat.models.Rooms.replaceUsername previousUsername, username
RocketChat.models.Rooms.replaceMutedUsername previousUsername, username
RocketChat.models.Rooms.replaceUsernameOfUserByUserId user._id, username
RocketChat.models.Subscriptions.setUserUsernameByUserId user._id, username
RocketChat.models.Subscriptions.setNameForDirectRoomsWithOldName previousUsername, username
rs = RocketChatFileAvatarInstance.getFileWithReadStream(encodeURIComponent("#{previousUsername}.jpg"))
if rs?
RocketChatFileAvatarInstance.deleteFile encodeURIComponent("#{username}.jpg")
ws = RocketChatFileAvatarInstance.createWriteStream encodeURIComponent("#{username}.jpg"), rs.contentType
ws.on 'end', Meteor.bindEnvironment ->
RocketChatFileAvatarInstance.deleteFile encodeURIComponent("#{previousUsername}.jpg")
rs.readStream.pipe(ws)
# Set new username
RocketChat.models.Users.setUsername user._id, username
return user
RocketChat.setUsername = RocketChat.RateLimiter.limitFunction RocketChat._setUsername, 1, 60000,
0: () -> return not Meteor.userId() or not RocketChat.authz.hasPermission(Meteor.userId(), 'edit-other-user-info') # Administrators have permission to change others usernames, so don't limit those

@ -0,0 +1,85 @@
RocketChat._setUsername = function(userId, u) {
const username = s.trim(u);
if (!userId || !username) {
return false;
}
let nameValidation;
try {
nameValidation = new RegExp(`^${ RocketChat.settings.get('UTF8_Names_Validation') }$`);
} catch (error) {
nameValidation = new RegExp('^[0-9a-zA-Z-_.]+$');
}
if (!nameValidation.test(username)) {
return false;
}
const user = RocketChat.models.Users.findOneById(userId);
// User already has desired username, return
if (user.username === username) {
return user;
}
const previousUsername = user.username;
// Check username availability or if the user already owns a different casing of the name
if (!previousUsername || !(username.toLowerCase() === previousUsername.toLowerCase())) {
if (!RocketChat.checkUsernameAvailability(username)) {
return false;
}
}
//If first time setting username, send Enrollment Email
try {
if (!previousUsername && user.emails && user.emails.length > 0 && RocketChat.settings.get('Accounts_Enrollment_Email')) {
Accounts.sendEnrollmentEmail(user._id);
}
} catch (e) {
console.error(e);
}
/* globals getAvatarSuggestionForUser */
user.username = username;
if (!previousUsername && RocketChat.settings.get('Accounts_SetDefaultAvatar') === true) {
const avatarSuggestions = getAvatarSuggestionForUser(user);
let gravatar;
Object.keys(avatarSuggestions).some(service => {
const avatarData = avatarSuggestions[service];
if (service !== 'gravatar') {
RocketChat.setUserAvatar(user, avatarData.blob, avatarData.contentType, service);
gravatar = null;
return true;
} else {
gravatar = avatarData;
}
});
if (gravatar != null) {
RocketChat.setUserAvatar(user, gravatar.blob, gravatar.contentType, 'gravatar');
}
}
// Username is available; if coming from old username, update all references
if (previousUsername) {
RocketChat.models.Messages.updateAllUsernamesByUserId(user._id, username);
RocketChat.models.Messages.updateUsernameOfEditByUserId(user._id, username);
RocketChat.models.Messages.findByMention(previousUsername).forEach(function(msg) {
const updatedMsg = msg.msg.replace(new RegExp(`@${ previousUsername }`, 'ig'), `@${ username }`);
return RocketChat.models.Messages.updateUsernameAndMessageOfMentionByIdAndOldUsername(msg._id, previousUsername, username, updatedMsg);
});
RocketChat.models.Rooms.replaceUsername(previousUsername, username);
RocketChat.models.Rooms.replaceMutedUsername(previousUsername, username);
RocketChat.models.Rooms.replaceUsernameOfUserByUserId(user._id, username);
RocketChat.models.Subscriptions.setUserUsernameByUserId(user._id, username);
RocketChat.models.Subscriptions.setNameForDirectRoomsWithOldName(previousUsername, username);
const rs = RocketChatFileAvatarInstance.getFileWithReadStream(encodeURIComponent(`${ previousUsername }.jpg`));
if (rs != null) {
RocketChatFileAvatarInstance.deleteFile(encodeURIComponent(`${ username }.jpg`));
const ws = RocketChatFileAvatarInstance.createWriteStream(encodeURIComponent(`${ username }.jpg`), rs.contentType);
ws.on('end', Meteor.bindEnvironment(() => RocketChatFileAvatarInstance.deleteFile(encodeURIComponent(`${ previousUsername }.jpg`))));
rs.readStream.pipe(ws);
}
}
// Set new username*
RocketChat.models.Users.setUsername(user._id, username);
return user;
};
RocketChat.setUsername = RocketChat.RateLimiter.limitFunction(RocketChat._setUsername, 1, 60000, {
[0](userId) {
return !userId || !RocketChat.authz.hasPermission(userId, 'edit-other-user-info');
}
});

@ -1,248 +0,0 @@
blockedSettings = {}
process.env.SETTINGS_BLOCKED?.split(',').forEach (settingId) ->
blockedSettings[settingId] = 1
hiddenSettings = {}
process.env.SETTINGS_HIDDEN?.split(',').forEach (settingId) ->
hiddenSettings[settingId] = 1
RocketChat.settings._sorter = {}
###
# Add a setting
# @param {String} _id
# @param {Mixed} value
# @param {Object} setting
###
RocketChat.settings.add = (_id, value, options = {}) ->
# console.log '[functions] RocketChat.settings.add -> '.green, 'arguments:', arguments
if not _id or
not value? and not process?.env?['OVERWRITE_SETTING_' + _id]?
return false
RocketChat.settings._sorter[options.group] ?= 0
options.packageValue = value
options.valueSource = 'packageValue'
options.hidden = false
options.blocked = options.blocked || false
options.sorter ?= RocketChat.settings._sorter[options.group]++
if options.enableQuery?
options.enableQuery = JSON.stringify options.enableQuery
if options.i18nDefaultQuery?
options.i18nDefaultQuery = JSON.stringify options.i18nDefaultQuery
if process?.env?[_id]?
value = process.env[_id]
if value.toLowerCase() is "true"
value = true
else if value.toLowerCase() is "false"
value = false
options.processEnvValue = value
options.valueSource = 'processEnvValue'
else if Meteor.settings?[_id]?
value = Meteor.settings[_id]
options.meteorSettingsValue = value
options.valueSource = 'meteorSettingsValue'
if not options.i18nLabel?
options.i18nLabel = _id
# Default description i18n key will be the setting name + "_Description" (eg: LDAP_Enable -> LDAP_Enable_Description)
if not options.i18nDescription?
options.i18nDescription = "#{_id}_Description"
if blockedSettings[_id]?
options.blocked = true
if hiddenSettings[_id]?
options.hidden = true
if process?.env?['OVERWRITE_SETTING_' + _id]?
value = process.env['OVERWRITE_SETTING_' + _id]
if value.toLowerCase() is "true"
value = true
else if value.toLowerCase() is "false"
value = false
options.value = value
options.processEnvValue = value
options.valueSource = 'processEnvValue'
updateOperations =
$set: options
$setOnInsert:
createdAt: new Date
if options.editor?
updateOperations.$setOnInsert.editor = options.editor
delete options.editor
if not options.value?
if options.force is true
updateOperations.$set.value = options.packageValue
else
updateOperations.$setOnInsert.value = value
query = _.extend { _id: _id }, updateOperations.$set
if not options.section?
updateOperations.$unset = { section: 1 }
query.section = { $exists: false }
existantSetting = RocketChat.models.Settings.db.findOne(query)
if existantSetting?
if not existantSetting.editor? and updateOperations.$setOnInsert.editor?
updateOperations.$set.editor = updateOperations.$setOnInsert.editor
delete updateOperations.$setOnInsert.editor
else
updateOperations.$set.ts = new Date
return RocketChat.models.Settings.upsert { _id: _id }, updateOperations
###
# Add a setting group
# @param {String} _id
###
RocketChat.settings.addGroup = (_id, options = {}, cb) ->
# console.log '[functions] RocketChat.settings.addGroup -> '.green, 'arguments:', arguments
if not _id
return false
if _.isFunction(options)
cb = options
options = {}
if not options.i18nLabel?
options.i18nLabel = _id
if not options.i18nDescription?
options.i18nDescription = "#{_id}_Description"
options.ts = new Date
options.blocked = false
options.hidden = false
if blockedSettings[_id]?
options.blocked = true
if hiddenSettings[_id]?
options.hidden = true
RocketChat.models.Settings.upsert { _id: _id },
$set: options
$setOnInsert:
type: 'group'
createdAt: new Date
if cb?
cb.call
add: (id, value, options = {}) ->
options.group = _id
RocketChat.settings.add id, value, options
section: (section, cb) ->
cb.call
add: (id, value, options = {}) ->
options.group = _id
options.section = section
RocketChat.settings.add id, value, options
return
###
# Remove a setting by id
# @param {String} _id
###
RocketChat.settings.removeById = (_id) ->
# console.log '[functions] RocketChat.settings.add -> '.green, 'arguments:', arguments
if not _id
return false
return RocketChat.models.Settings.removeById _id
###
# Update a setting by id
# @param {String} _id
###
RocketChat.settings.updateById = (_id, value, editor) ->
# console.log '[functions] RocketChat.settings.updateById -> '.green, 'arguments:', arguments
if not _id or not value?
return false
if editor?
return RocketChat.models.Settings.updateValueAndEditorById _id, value, editor
return RocketChat.models.Settings.updateValueById _id, value
###
# Update options of a setting by id
# @param {String} _id
###
RocketChat.settings.updateOptionsById = (_id, options) ->
# console.log '[functions] RocketChat.settings.updateOptionsById -> '.green, 'arguments:', arguments
if not _id or not options?
return false
return RocketChat.models.Settings.updateOptionsById _id, options
###
# Update a setting by id
# @param {String} _id
###
RocketChat.settings.clearById = (_id) ->
# console.log '[functions] RocketChat.settings.clearById -> '.green, 'arguments:', arguments
if not _id?
return false
return RocketChat.models.Settings.updateValueById _id, undefined
###
# Update a setting by id
###
RocketChat.settings.init = ->
RocketChat.settings.initialLoad = true
RocketChat.models.Settings.find().observe
added: (record) ->
Meteor.settings[record._id] = record.value
if record.env is true
process.env[record._id] = record.value
RocketChat.settings.load record._id, record.value, RocketChat.settings.initialLoad
changed: (record) ->
Meteor.settings[record._id] = record.value
if record.env is true
process.env[record._id] = record.value
RocketChat.settings.load record._id, record.value, RocketChat.settings.initialLoad
removed: (record) ->
delete Meteor.settings[record._id]
if record.env is true
delete process.env[record._id]
RocketChat.settings.load record._id, undefined, RocketChat.settings.initialLoad
RocketChat.settings.initialLoad = false
for fn in RocketChat.settings.afterInitialLoad
fn(Meteor.settings)
RocketChat.settings.afterInitialLoad = []
RocketChat.settings.onAfterInitialLoad = (fn) ->
RocketChat.settings.afterInitialLoad.push(fn)
if RocketChat.settings.initialLoad is false
fn(Meteor.settings)

@ -0,0 +1,283 @@
const blockedSettings = {};
if (process.env.SETTINGS_BLOCKED) {
process.env.SETTINGS_BLOCKED.split(',').forEach((settingId) => blockedSettings[settingId] = 1);
}
const hiddenSettings = {};
if (process.env.SETTINGS_HIDDEN) {
process.env.SETTINGS_HIDDEN.split(',').forEach((settingId) => hiddenSettings[settingId] = 1);
}
RocketChat.settings._sorter = {};
/*
* Add a setting
* @param {String} _id
* @param {Mixed} value
* @param {Object} setting
*/
RocketChat.settings.add = function(_id, value, options = {}) {
if (options == null) {
options = {};
}
if (!_id || value == null) {
return false;
}
if (RocketChat.settings._sorter[options.group] == null) {
RocketChat.settings._sorter[options.group] = 0;
}
options.packageValue = value;
options.valueSource = 'packageValue';
options.hidden = false;
options.blocked = options.blocked || false;
if (options.sorter == null) {
options.sorter = RocketChat.settings._sorter[options.group]++;
}
if (options.enableQuery != null) {
options.enableQuery = JSON.stringify(options.enableQuery);
}
if (options.i18nDefaultQuery != null) {
options.i18nDefaultQuery = JSON.stringify(options.i18nDefaultQuery);
}
if (typeof process !== 'undefined' && process.env && process.env._id) {
let value = process.env[_id];
if (value.toLowerCase() === 'true') {
value = true;
} else if (value.toLowerCase() === 'false') {
value = false;
}
options.processEnvValue = value;
options.valueSource = 'processEnvValue';
} else if (Meteor.settings && Meteor.settings) {
const value = Meteor.settings[_id];
options.meteorSettingsValue = value;
options.valueSource = 'meteorSettingsValue';
}
if (options.i18nLabel == null) {
options.i18nLabel = _id;
}
if (options.i18nDescription == null) {
options.i18nDescription = `${ _id }_Description`;
}
if (blockedSettings[_id] != null) {
options.blocked = true;
}
if (hiddenSettings[_id] != null) {
options.hidden = true;
}
if (typeof process !== 'undefined' && process.env && process.env[`OVERWRITE_SETTING_${ _id }`]) {
let value = process.env[`OVERWRITE_SETTING_${ _id }`];
if (value.toLowerCase() === 'true') {
value = true;
} else if (value.toLowerCase() === 'false') {
value = false;
}
options.value = value;
options.processEnvValue = value;
options.valueSource = 'processEnvValue';
}
const updateOperations = {
$set: options,
$setOnInsert: {
createdAt: new Date
}
};
if (options.editor != null) {
updateOperations.$setOnInsert.editor = options.editor;
delete options.editor;
}
if (options.value == null) {
if (options.force === true) {
updateOperations.$set.value = options.packageValue;
} else {
updateOperations.$setOnInsert.value = value;
}
}
const query = _.extend({
_id
}, updateOperations.$set);
if (options.section == null) {
updateOperations.$unset = {
section: 1
};
query.section = {
$exists: false
};
}
const existantSetting = RocketChat.models.Settings.db.findOne(query);
if (existantSetting != null) {
if (existantSetting.editor == null && updateOperations.$setOnInsert.editor != null) {
updateOperations.$set.editor = updateOperations.$setOnInsert.editor;
delete updateOperations.$setOnInsert.editor;
}
} else {
updateOperations.$set.ts = new Date;
}
return RocketChat.models.Settings.upsert({
_id
}, updateOperations);
};
/*
* Add a setting group
* @param {String} _id
*/
RocketChat.settings.addGroup = function(_id, options = {}, cb) {
if (!_id) {
return false;
}
if (_.isFunction(options)) {
cb = options;
options = {};
}
if (options.i18nLabel == null) {
options.i18nLabel = _id;
}
if (options.i18nDescription == null) {
options.i18nDescription = `${ _id }_Description`;
}
options.ts = new Date;
options.blocked = false;
options.hidden = false;
if (blockedSettings[_id] != null) {
options.blocked = true;
}
if (hiddenSettings[_id] != null) {
options.hidden = true;
}
RocketChat.models.Settings.upsert({
_id
}, {
$set: options,
$setOnInsert: {
type: 'group',
createdAt: new Date
}
});
if (cb != null) {
cb.call({
add(id, value, options) {
if (options == null) {
options = {};
}
options.group = _id;
return RocketChat.settings.add(id, value, options);
},
section(section, cb) {
return cb.call({
add(id, value, options) {
if (options == null) {
options = {};
}
options.group = _id;
options.section = section;
return RocketChat.settings.add(id, value, options);
}
});
}
});
}
};
/*
* Remove a setting by id
* @param {String} _id
*/
RocketChat.settings.removeById = function(_id) {
if (!_id) {
return false;
}
return RocketChat.models.Settings.removeById(_id);
};
/*
* Update a setting by id
* @param {String} _id
*/
RocketChat.settings.updateById = function(_id, value, editor) {
if (!_id || value == null) {
return false;
}
if (editor != null) {
return RocketChat.models.Settings.updateValueAndEditorById(_id, value, editor);
}
return RocketChat.models.Settings.updateValueById(_id, value);
};
/*
* Update options of a setting by id
* @param {String} _id
*/
RocketChat.settings.updateOptionsById = function(_id, options) {
if (!_id || options == null) {
return false;
}
return RocketChat.models.Settings.updateOptionsById(_id, options);
};
/*
* Update a setting by id
* @param {String} _id
*/
RocketChat.settings.clearById = function(_id) {
if (_id == null) {
return false;
}
return RocketChat.models.Settings.updateValueById(_id, undefined);
};
/*
* Update a setting by id
*/
RocketChat.settings.init = function() {
RocketChat.settings.initialLoad = true;
RocketChat.models.Settings.find().observe({
added(record) {
Meteor.settings[record._id] = record.value;
if (record.env === true) {
process.env[record._id] = record.value;
}
return RocketChat.settings.load(record._id, record.value, RocketChat.settings.initialLoad);
},
changed(record) {
Meteor.settings[record._id] = record.value;
if (record.env === true) {
process.env[record._id] = record.value;
}
return RocketChat.settings.load(record._id, record.value, RocketChat.settings.initialLoad);
},
removed(record) {
delete Meteor.settings[record._id];
if (record.env === true) {
delete process.env[record._id];
}
return RocketChat.settings.load(record._id, undefined, RocketChat.settings.initialLoad);
}
});
RocketChat.settings.initialLoad = false;
RocketChat.settings.afterInitialLoad.forEach(fn => fn(Meteor.settings));
};
RocketChat.settings.afterInitialLoad = [];
RocketChat.settings.onAfterInitialLoad = function(fn) {
RocketChat.settings.afterInitialLoad.push(fn);
if (RocketChat.settings.initialLoad === false) {
return fn(Meteor.settings);
}
};

@ -1,64 +0,0 @@
import moment from 'moment'
Meteor.methods
sendMessage: (message) ->
check message, Object
if not Meteor.userId()
throw new Meteor.Error('error-invalid-user', "Invalid user", { method: 'sendMessage' })
if message.ts
tsDiff = Math.abs(moment(message.ts).diff())
if tsDiff > 60000
throw new Meteor.Error('error-message-ts-out-of-sync', 'Message timestamp is out of sync', { method: 'sendMessage', message_ts: message.ts, server_ts: new Date().getTime() })
else if tsDiff > 10000
message.ts = new Date()
else
message.ts = new Date()
if message.msg?.length > RocketChat.settings.get('Message_MaxAllowedSize')
throw new Meteor.Error('error-message-size-exceeded', 'Message size exceeds Message_MaxAllowedSize', { method: 'sendMessage' })
user = RocketChat.models.Users.findOneById Meteor.userId(), fields: username: 1, name: 1
room = Meteor.call 'canAccessRoom', message.rid, user._id
if not room
return false
subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(message.rid, Meteor.userId());
if subscription and (subscription.blocked or subscription.blocker)
RocketChat.Notifications.notifyUser Meteor.userId(), 'message', {
_id: Random.id()
rid: room._id
ts: new Date
msg: TAPi18n.__('room_is_blocked', {}, user.language)
}
return false
if user.username in (room.muted or [])
RocketChat.Notifications.notifyUser Meteor.userId(), 'message', {
_id: Random.id()
rid: room._id
ts: new Date
msg: TAPi18n.__('You_have_been_muted', {}, user.language)
}
return false
message.alias = user.name if not message.alias? and RocketChat.settings.get 'Message_SetNameToAliasEnabled'
if Meteor.settings.public.sandstorm
message.sandstormSessionId = this.connection.sandstormSessionId()
RocketChat.metrics.messagesSent.inc() # This line needs to be moved to it's proper place. See the comments on: https://github.com/RocketChat/Rocket.Chat/pull/5736
RocketChat.sendMessage user, message, room
# Limit a user, who does not have the "bot" role, to sending 5 msgs/second
DDPRateLimiter.addRule
type: 'method'
name: 'sendMessage'
userId: (userId) ->
user = RocketChat.models.Users.findOneById(userId)
return true if not user?.roles
return 'bot' not in user.roles
, 5, 1000

@ -0,0 +1,81 @@
import moment from 'moment';
Meteor.methods({
sendMessage(message) {
check(message, Object);
if (!Meteor.userId()) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
method: 'sendMessage'
});
}
if (message.ts) {
const tsDiff = Math.abs(moment(message.ts).diff());
if (tsDiff > 60000) {
throw new Meteor.Error('error-message-ts-out-of-sync', 'Message timestamp is out of sync', {
method: 'sendMessage',
message_ts: message.ts,
server_ts: new Date().getTime()
});
} else if (tsDiff > 10000) {
message.ts = new Date();
}
} else {
message.ts = new Date();
}
if (message.msg && message.msg.length > RocketChat.settings.get('Message_MaxAllowedSize')) {
throw new Meteor.Error('error-message-size-exceeded', 'Message size exceeds Message_MaxAllowedSize', {
method: 'sendMessage'
});
}
const user = RocketChat.models.Users.findOneById(Meteor.userId(), {
fields: {
username: 1,
name: 1
}
});
const room = Meteor.call('canAccessRoom', message.rid, user._id);
if (!room) {
return false;
}
const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(message.rid, Meteor.userId());
if (subscription && subscription.blocked || subscription.blocker) {
RocketChat.Notifications.notifyUser(Meteor.userId(), 'message', {
_id: Random.id(),
rid: room._id,
ts: new Date,
msg: TAPi18n.__('room_is_blocked', {}, user.language)
});
return false;
}
if ((room.muted||[]).includes(user.username)) {
RocketChat.Notifications.notifyUser(Meteor.userId(), 'message', {
_id: Random.id(),
rid: room._id,
ts: new Date,
msg: TAPi18n.__('You_have_been_muted', {}, user.language)
});
return false;
}
if (message.alias == null && RocketChat.settings.get('Message_SetNameToAliasEnabled')) {
message.alias = user.name;
}
if (Meteor.settings['public'].sandstorm) {
message.sandstormSessionId = this.connection.sandstormSessionId();
}
RocketChat.metrics.messagesSent.inc(); // TODO This line needs to be moved to it's proper place. See the comments on: https://github.com/RocketChat/Rocket.Chat/pull/5736
return RocketChat.sendMessage(user, message, room);
}
});
// Limit a user, who does not have the "bot" role, to sending 5 msgs/second
DDPRateLimiter.addRule({
type: 'method',
name: 'sendMessage',
userId(userId) {
const user = RocketChat.models.Users.findOneById(userId);
if (user == null || !user.roles) {
return true;
}
return user.roles.includes('bot');
}
}, 5, 1000);

@ -0,0 +1,580 @@
RocketChat.models.Messages = new class extends RocketChat.models._Base {
constructor() {
super('message');
this.tryEnsureIndex({ 'rid': 1, 'ts': 1 });
this.tryEnsureIndex({ 'ts': 1 });
this.tryEnsureIndex({ 'u._id': 1 });
this.tryEnsureIndex({ 'editedAt': 1 }, { sparse: 1 });
this.tryEnsureIndex({ 'editedBy._id': 1 }, { sparse: 1 });
this.tryEnsureIndex({ 'rid': 1, 't': 1, 'u._id': 1 });
this.tryEnsureIndex({ 'expireAt': 1 }, { expireAfterSeconds: 0 });
this.tryEnsureIndex({ 'msg': 'text' });
this.tryEnsureIndex({ 'file._id': 1 }, { sparse: 1 });
this.tryEnsureIndex({ 'mentions.username': 1 }, { sparse: 1 });
this.tryEnsureIndex({ 'pinned': 1 }, { sparse: 1 });
this.tryEnsureIndex({ 'snippeted': 1 }, { sparse: 1 });
this.tryEnsureIndex({ 'location': '2dsphere' });
this.tryEnsureIndex({ 'slackBotId': 1, 'slackTs': 1 }, { sparse: 1 });
}
// FIND
findByMention(username, options) {
const query = {'mentions.username': username};
return this.find(query, options);
}
findVisibleByMentionAndRoomId(username, rid, options) {
const query = {
_hidden: { $ne: true },
'mentions.username': username,
rid
};
return this.find(query, options);
}
findVisibleByRoomId(roomId, options) {
const query = {
_hidden: {
$ne: true
},
rid: roomId
};
return this.find(query, options);
}
findVisibleByRoomIdNotContainingTypes(roomId, types, options) {
const query = {
_hidden: {
$ne: true
},
rid: roomId
};
if (Match.test(types, [String]) && (types.length > 0)) {
query.t =
{$nin: types};
}
return this.find(query, options);
}
findInvisibleByRoomId(roomId, options) {
const query = {
_hidden: true,
rid: roomId
};
return this.find(query, options);
}
findVisibleByRoomIdAfterTimestamp(roomId, timestamp, options) {
const query = {
_hidden: {
$ne: true
},
rid: roomId,
ts: {
$gt: timestamp
}
};
return this.find(query, options);
}
findVisibleByRoomIdBeforeTimestamp(roomId, timestamp, options) {
const query = {
_hidden: {
$ne: true
},
rid: roomId,
ts: {
$lt: timestamp
}
};
return this.find(query, options);
}
findVisibleByRoomIdBeforeTimestampInclusive(roomId, timestamp, options) {
const query = {
_hidden: {
$ne: true
},
rid: roomId,
ts: {
$lte: timestamp
}
};
return this.find(query, options);
}
findVisibleByRoomIdBetweenTimestamps(roomId, afterTimestamp, beforeTimestamp, options) {
const query = {
_hidden: {
$ne: true
},
rid: roomId,
ts: {
$gt: afterTimestamp,
$lt: beforeTimestamp
}
};
return this.find(query, options);
}
findVisibleByRoomIdBetweenTimestampsInclusive(roomId, afterTimestamp, beforeTimestamp, options) {
const query = {
_hidden: {
$ne: true
},
rid: roomId,
ts: {
$gte: afterTimestamp,
$lte: beforeTimestamp
}
};
return this.find(query, options);
}
findVisibleByRoomIdBeforeTimestampNotContainingTypes(roomId, timestamp, types, options) {
const query = {
_hidden: {
$ne: true
},
rid: roomId,
ts: {
$lt: timestamp
}
};
if (Match.test(types, [String]) && (types.length > 0)) {
query.t =
{$nin: types};
}
return this.find(query, options);
}
findVisibleByRoomIdBetweenTimestampsNotContainingTypes(roomId, afterTimestamp, beforeTimestamp, types, options) {
const query = {
_hidden: {
$ne: true
},
rid: roomId,
ts: {
$gt: afterTimestamp,
$lt: beforeTimestamp
}
};
if (Match.test(types, [String]) && (types.length > 0)) {
query.t =
{$nin: types};
}
return this.find(query, options);
}
findVisibleCreatedOrEditedAfterTimestamp(timestamp, options) {
const query = {
_hidden: { $ne: true },
$or: [{
ts: {
$gt: timestamp
}
},
{
'editedAt': {
$gt: timestamp
}
}
]
};
return this.find(query, options);
}
findStarredByUserAtRoom(userId, roomId, options) {
const query = {
_hidden: { $ne: true },
'starred._id': userId,
rid: roomId
};
return this.find(query, options);
}
findPinnedByRoom(roomId, options) {
const query = {
t: { $ne: 'rm' },
_hidden: { $ne: true },
pinned: true,
rid: roomId
};
return this.find(query, options);
}
findSnippetedByRoom(roomId, options) {
const query = {
_hidden: { $ne: true },
snippeted: true,
rid: roomId
};
return this.find(query, options);
}
getLastTimestamp(options) {
if (options == null) { options = {}; }
const query = { ts: { $exists: 1 } };
options.sort = { ts: -1 };
options.limit = 1;
const [message] = this.find(query, options).fetch();
return message && message.ts;
}
findByRoomIdAndMessageIds(rid, messageIds, options) {
const query = {
rid,
_id: {
$in: messageIds
}
};
return this.find(query, options);
}
findOneBySlackBotIdAndSlackTs(slackBotId, slackTs) {
const query = {
slackBotId,
slackTs
};
return this.findOne(query);
}
findOneBySlackTs(slackTs) {
const query = {slackTs};
return this.findOne(query);
}
cloneAndSaveAsHistoryById(_id) {
const me = RocketChat.models.Users.findOneById(Meteor.userId());
const record = this.findOneById(_id);
record._hidden = true;
record.parent = record._id;
record.editedAt = new Date;
record.editedBy = {
_id: Meteor.userId(),
username: me.username
};
delete record._id;
return this.insert(record);
}
// UPDATE
setHiddenById(_id, hidden) {
if (hidden == null) { hidden = true; }
const query = {_id};
const update = {
$set: {
_hidden: hidden
}
};
return this.update(query, update);
}
setAsDeletedByIdAndUser(_id, user) {
const query = {_id};
const update = {
$set: {
msg: '',
t: 'rm',
urls: [],
mentions: [],
attachments: [],
reactions: [],
editedAt: new Date(),
editedBy: {
_id: user._id,
username: user.username
}
}
};
return this.update(query, update);
}
setPinnedByIdAndUserId(_id, pinnedBy, pinned, pinnedAt) {
if (pinned == null) { pinned = true; }
if (pinnedAt == null) { pinnedAt = 0; }
const query = {_id};
const update = {
$set: {
pinned,
pinnedAt: pinnedAt || new Date,
pinnedBy
}
};
return this.update(query, update);
}
setSnippetedByIdAndUserId(message, snippetName, snippetedBy, snippeted, snippetedAt) {
if (snippeted == null) { snippeted = true; }
if (snippetedAt == null) { snippetedAt = 0; }
const query = {_id: message._id};
const msg = `\`\`\`${ message.msg }\`\`\``;
const update = {
$set: {
msg,
snippeted,
snippetedAt: snippetedAt || new Date,
snippetedBy,
snippetName
}
};
return this.update(query, update);
}
setUrlsById(_id, urls) {
const query = {_id};
const update = {
$set: {
urls
}
};
return this.update(query, update);
}
updateAllUsernamesByUserId(userId, username) {
const query = {'u._id': userId};
const update = {
$set: {
'u.username': username
}
};
return this.update(query, update, { multi: true });
}
updateUsernameOfEditByUserId(userId, username) {
const query = {'editedBy._id': userId};
const update = {
$set: {
'editedBy.username': username
}
};
return this.update(query, update, { multi: true });
}
updateUsernameAndMessageOfMentionByIdAndOldUsername(_id, oldUsername, newUsername, newMessage) {
const query = {
_id,
'mentions.username': oldUsername
};
const update = {
$set: {
'mentions.$.username': newUsername,
'msg': newMessage
}
};
return this.update(query, update);
}
updateUserStarById(_id, userId, starred) {
let update;
const query = {_id};
if (starred) {
update = {
$addToSet: {
starred: { _id: userId }
}
};
} else {
update = {
$pull: {
starred: { _id: Meteor.userId() }
}
};
}
return this.update(query, update);
}
upgradeEtsToEditAt() {
const query = {ets: { $exists: 1 }};
const update = {
$rename: {
'ets': 'editedAt'
}
};
return this.update(query, update, { multi: true });
}
setMessageAttachments(_id, attachments) {
const query = {_id};
const update = {
$set: {
attachments
}
};
return this.update(query, update);
}
setSlackBotIdAndSlackTs(_id, slackBotId, slackTs) {
const query = {_id};
const update = {
$set: {
slackBotId,
slackTs
}
};
return this.update(query, update);
}
// INSERT
createWithTypeRoomIdMessageAndUser(type, roomId, message, user, extraData) {
const room = RocketChat.models.Rooms.findOneById(roomId, { fields: { sysMes: 1 }});
if ((room != null ? room.sysMes : undefined) === false) {
return;
}
const record = {
t: type,
rid: roomId,
ts: new Date,
msg: message,
u: {
_id: user._id,
username: user.username
},
groupable: false
};
_.extend(record, extraData);
record._id = this.insertOrUpsert(record);
RocketChat.models.Rooms.incMsgCountById(room._id, 1);
return record;
}
createUserJoinWithRoomIdAndUser(roomId, user, extraData) {
const message = user.username;
return this.createWithTypeRoomIdMessageAndUser('uj', roomId, message, user, extraData);
}
createUserLeaveWithRoomIdAndUser(roomId, user, extraData) {
const message = user.username;
return this.createWithTypeRoomIdMessageAndUser('ul', roomId, message, user, extraData);
}
createUserRemovedWithRoomIdAndUser(roomId, user, extraData) {
const message = user.username;
return this.createWithTypeRoomIdMessageAndUser('ru', roomId, message, user, extraData);
}
createUserAddedWithRoomIdAndUser(roomId, user, extraData) {
const message = user.username;
return this.createWithTypeRoomIdMessageAndUser('au', roomId, message, user, extraData);
}
createCommandWithRoomIdAndUser(command, roomId, user, extraData) {
return this.createWithTypeRoomIdMessageAndUser('command', roomId, command, user, extraData);
}
createUserMutedWithRoomIdAndUser(roomId, user, extraData) {
const message = user.username;
return this.createWithTypeRoomIdMessageAndUser('user-muted', roomId, message, user, extraData);
}
createUserUnmutedWithRoomIdAndUser(roomId, user, extraData) {
const message = user.username;
return this.createWithTypeRoomIdMessageAndUser('user-unmuted', roomId, message, user, extraData);
}
createNewModeratorWithRoomIdAndUser(roomId, user, extraData) {
const message = user.username;
return this.createWithTypeRoomIdMessageAndUser('new-moderator', roomId, message, user, extraData);
}
createModeratorRemovedWithRoomIdAndUser(roomId, user, extraData) {
const message = user.username;
return this.createWithTypeRoomIdMessageAndUser('moderator-removed', roomId, message, user, extraData);
}
createNewOwnerWithRoomIdAndUser(roomId, user, extraData) {
const message = user.username;
return this.createWithTypeRoomIdMessageAndUser('new-owner', roomId, message, user, extraData);
}
createOwnerRemovedWithRoomIdAndUser(roomId, user, extraData) {
const message = user.username;
return this.createWithTypeRoomIdMessageAndUser('owner-removed', roomId, message, user, extraData);
}
createSubscriptionRoleAddedWithRoomIdAndUser(roomId, user, extraData) {
const message = user.username;
return this.createWithTypeRoomIdMessageAndUser('subscription-role-added', roomId, message, user, extraData);
}
createSubscriptionRoleRemovedWithRoomIdAndUser(roomId, user, extraData) {
const message = user.username;
return this.createWithTypeRoomIdMessageAndUser('subscription-role-removed', roomId, message, user, extraData);
}
// REMOVE
removeById(_id) {
const query = {_id};
return this.remove(query);
}
removeByRoomId(roomId) {
const query = {rid: roomId};
return this.remove(query);
}
removeByUserId(userId) {
const query = {'u._id': userId};
return this.remove(query);
}
getMessageByFileId(fileID) {
return this.findOne({ 'file._id': fileID });
}
};

@ -0,0 +1,760 @@
class ModelRooms extends RocketChat.models._Base {
constructor() {
super(...arguments);
this.tryEnsureIndex({ 'name': 1 }, { unique: 1, sparse: 1 });
this.tryEnsureIndex({ 'default': 1 });
this.tryEnsureIndex({ 'usernames': 1 });
this.tryEnsureIndex({ 't': 1 });
this.tryEnsureIndex({ 'u._id': 1 });
this.cache.ignoreUpdatedFields.push('msgs', 'lm');
this.cache.ensureIndex(['t', 'name'], 'unique');
this.cache.options = {fields: {usernames: 0}};
}
findOneByIdOrName(_idOrName, options) {
const query = {
$or: [{
_id: _idOrName
}, {
name: _idOrName
}]
};
return this.findOne(query, options);
}
findOneByImportId(_id, options) {
const query = {importIds: _id};
return this.findOne(query, options);
}
findOneByName(name, options) {
const query = {name};
return this.findOne(query, options);
}
findOneByNameAndType(name, type, options) {
const query = {
name,
t: type
};
return this.findOne(query, options);
}
findOneByIdContainingUsername(_id, username, options) {
const query = {
_id,
usernames: username
};
return this.findOne(query, options);
}
findOneByNameAndTypeNotContainingUsername(name, type, username, options) {
const query = {
name,
t: type,
usernames: {
$ne: username
}
};
return this.findOne(query, options);
}
// FIND
findById(roomId, options) {
return this.find({ _id: roomId }, options);
}
findByIds(roomIds, options) {
return this.find({ _id: {$in: [].concat(roomIds)} }, options);
}
findByType(type, options) {
const query = {t: type};
return this.find(query, options);
}
findByTypes(types, options) {
const query = {
t: {
$in: types
}
};
return this.find(query, options);
}
findByUserId(userId, options) {
const query = {'u._id': userId};
return this.find(query, options);
}
findBySubscriptionUserId(userId, options) {
let data;
if (this.useCache) {
data = RocketChat.models.Subscriptions.findByUserId(userId).fetch();
data = data.map(function(item) {
if (item._room) {
return item._room;
}
console.log('Empty Room for Subscription', item);
return {};
});
return this.arrayToCursor(this.processQueryOptionsOnResult(data, options));
}
data = RocketChat.models.Subscriptions.findByUserId(userId, {fields: {rid: 1}}).fetch();
data = data.map(item => item.rid);
const query = {
_id: {
$in: data
}
};
return this.find(query, options);
}
findBySubscriptionUserIdUpdatedAfter(userId, _updatedAt, options) {
if (this.useCache) {
let data = RocketChat.models.Subscriptions.findByUserId(userId).fetch();
data = data.map(function(item) {
if (item._room) {
return item._room;
}
console.log('Empty Room for Subscription', item);
return {};
});
data = data.filter(item => item._updatedAt > _updatedAt);
return this.arrayToCursor(this.processQueryOptionsOnResult(data, options));
}
let ids = RocketChat.models.Subscriptions.findByUserId(userId, {fields: {rid: 1}}).fetch();
ids = ids.map(item => item.rid);
const query = {
_id: {
$in: ids
},
_updatedAt: {
$gt: _updatedAt
}
};
return this.find(query, options);
}
findByNameContaining(name, options) {
const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i');
const query = {
$or: [
{name: nameRegex},
{
t: 'd',
usernames: nameRegex
}
]
};
return this.find(query, options);
}
findByNameContainingTypesWithUsername(name, types, options) {
const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i');
const $or = [];
for (const type of Array.from(types)) {
const obj = {name: nameRegex, t: type.type};
if (type.username != null) {
obj.usernames = type.username;
}
if (type.ids != null) {
obj._id = {$in: type.ids};
}
$or.push(obj);
}
const query = {$or};
return this.find(query, options);
}
findContainingTypesWithUsername(types, options) {
const $or = [];
for (const type of Array.from(types)) {
const obj = {t: type.type};
if (type.username != null) {
obj.usernames = type.username;
}
if (type.ids != null) {
obj._id = {$in: type.ids};
}
$or.push(obj);
}
const query = {$or};
return this.find(query, options);
}
findByNameContainingAndTypes(name, types, options) {
const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i');
const query = {
t: {
$in: types
},
$or: [
{name: nameRegex},
{
t: 'd',
usernames: nameRegex
}
]
};
return this.find(query, options);
}
findByNameAndTypeNotContainingUsername(name, type, username, options) {
const query = {
t: type,
name,
usernames: {
$ne: username
}
};
return this.find(query, options);
}
findByNameStartingAndTypes(name, types, options) {
const nameRegex = new RegExp(`^${ s.trim(s.escapeRegExp(name)) }`, 'i');
const query = {
t: {
$in: types
},
$or: [
{name: nameRegex},
{
t: 'd',
usernames: nameRegex
}
]
};
return this.find(query, options);
}
findByDefaultAndTypes(defaultValue, types, options) {
const query = {
default: defaultValue,
t: {
$in: types
}
};
return this.find(query, options);
}
findByTypeContainingUsername(type, username, options) {
const query = {
t: type,
usernames: username
};
return this.find(query, options);
}
findByTypeContainingUsernames(type, username, options) {
const query = {
t: type,
usernames: { $all: [].concat(username) }
};
return this.find(query, options);
}
findByTypesAndNotUserIdContainingUsername(types, userId, username, options) {
const query = {
t: {
$in: types
},
uid: {
$ne: userId
},
usernames: username
};
return this.find(query, options);
}
findByContainingUsername(username, options) {
const query = {usernames: username};
return this.find(query, options);
}
findByTypeAndName(type, name, options) {
if (this.useCache) {
return this.cache.findByIndex('t,name', [type, name], options);
}
const query = {
name,
t: type
};
return this.find(query, options);
}
findByTypeAndNameContainingUsername(type, name, username, options) {
const query = {
name,
t: type,
usernames: username
};
return this.find(query, options);
}
findByTypeAndArchivationState(type, archivationstate, options) {
const query = {t: type};
if (archivationstate) {
query.archived = true;
} else {
query.archived = { $ne: true };
}
return this.find(query, options);
}
// UPDATE
addImportIds(_id, importIds) {
importIds = [].concat(importIds);
const query = {_id};
const update = {
$addToSet: {
importIds: {
$each: importIds
}
}
};
return this.update(query, update);
}
archiveById(_id) {
const query = {_id};
const update = {
$set: {
archived: true
}
};
return this.update(query, update);
}
unarchiveById(_id) {
const query = {_id};
const update = {
$set: {
archived: false
}
};
return this.update(query, update);
}
addUsernameById(_id, username, muted) {
const query = {_id};
const update = {
$addToSet: {
usernames: username
}
};
if (muted) {
update.$addToSet.muted = username;
}
return this.update(query, update);
}
addUsernamesById(_id, usernames) {
const query = {_id};
const update = {
$addToSet: {
usernames: {
$each: usernames
}
}
};
return this.update(query, update);
}
addUsernameByName(name, username) {
const query = {name};
const update = {
$addToSet: {
usernames: username
}
};
return this.update(query, update);
}
removeUsernameById(_id, username) {
const query = {_id};
const update = {
$pull: {
usernames: username
}
};
return this.update(query, update);
}
removeUsernamesById(_id, usernames) {
const query = {_id};
const update = {
$pull: {
usernames: {
$in: usernames
}
}
};
return this.update(query, update);
}
removeUsernameFromAll(username) {
const query = {usernames: username};
const update = {
$pull: {
usernames: username
}
};
return this.update(query, update, { multi: true });
}
removeUsernameByName(name, username) {
const query = {name};
const update = {
$pull: {
usernames: username
}
};
return this.update(query, update);
}
setNameById(_id, name) {
const query = {_id};
const update = {
$set: {
name
}
};
return this.update(query, update);
}
incMsgCountById(_id, inc) {
if (inc == null) { inc = 1; }
const query = {_id};
const update = {
$inc: {
msgs: inc
}
};
return this.update(query, update);
}
incMsgCountAndSetLastMessageTimestampById(_id, inc, lastMessageTimestamp) {
if (inc == null) { inc = 1; }
const query = {_id};
const update = {
$set: {
lm: lastMessageTimestamp
},
$inc: {
msgs: inc
}
};
return this.update(query, update);
}
replaceUsername(previousUsername, username) {
const query = {usernames: previousUsername};
const update = {
$set: {
'usernames.$': username
}
};
return this.update(query, update, { multi: true });
}
replaceMutedUsername(previousUsername, username) {
const query = {muted: previousUsername};
const update = {
$set: {
'muted.$': username
}
};
return this.update(query, update, { multi: true });
}
replaceUsernameOfUserByUserId(userId, username) {
const query = {'u._id': userId};
const update = {
$set: {
'u.username': username
}
};
return this.update(query, update, { multi: true });
}
setJoinCodeById(_id, joinCode) {
let update;
const query = {_id};
if ((joinCode != null ? joinCode.trim() : undefined) !== '') {
update = {
$set: {
joinCodeRequired: true,
joinCode
}
};
} else {
update = {
$set: {
joinCodeRequired: false
},
$unset: {
joinCode: 1
}
};
}
return this.update(query, update);
}
setUserById(_id, user) {
const query = {_id};
const update = {
$set: {
u: {
_id: user._id,
username: user.username
}
}
};
return this.update(query, update);
}
setTypeById(_id, type) {
const query = {_id};
const update = {
$set: {
t: type
}
};
if (type === 'p') {
update.$unset = {default: ''};
}
return this.update(query, update);
}
setTopicById(_id, topic) {
const query = {_id};
const update = {
$set: {
topic
}
};
return this.update(query, update);
}
setAnnouncementById(_id, announcement) {
const query = {_id};
const update = {
$set: {
announcement
}
};
return this.update(query, update);
}
muteUsernameByRoomId(_id, username) {
const query = {_id};
const update = {
$addToSet: {
muted: username
}
};
return this.update(query, update);
}
unmuteUsernameByRoomId(_id, username) {
const query = {_id};
const update = {
$pull: {
muted: username
}
};
return this.update(query, update);
}
saveDefaultById(_id, defaultValue) {
const query = {_id};
const update = {
$set: {
default: defaultValue === 'true'
}
};
return this.update(query, update);
}
setTopicAndTagsById(_id, topic, tags) {
const setData = {};
const unsetData = {};
if (topic != null) {
if (!_.isEmpty(s.trim(topic))) {
setData.topic = s.trim(topic);
} else {
unsetData.topic = 1;
}
}
if (tags != null) {
if (!_.isEmpty(s.trim(tags))) {
setData.tags = s.trim(tags).split(',').map(tag => s.trim(tag));
} else {
unsetData.tags = 1;
}
}
const update = {};
if (!_.isEmpty(setData)) {
update.$set = setData;
}
if (!_.isEmpty(unsetData)) {
update.$unset = unsetData;
}
if (_.isEmpty(update)) {
return;
}
return this.update({ _id }, update);
}
// INSERT
createWithTypeNameUserAndUsernames(type, name, user, usernames, extraData) {
const room = {
name,
t: type,
usernames,
msgs: 0,
u: {
_id: user._id,
username: user.username
}
};
_.extend(room, extraData);
room._id = this.insert(room);
return room;
}
createWithIdTypeAndName(_id, type, name, extraData) {
const room = {
_id,
ts: new Date(),
t: type,
name,
usernames: [],
msgs: 0
};
_.extend(room, extraData);
this.insert(room);
return room;
}
// REMOVE
removeById(_id) {
const query = {_id};
return this.remove(query);
}
removeByTypeContainingUsername(type, username) {
const query = {
t: type,
usernames: username
};
return this.remove(query);
}
}
RocketChat.models.Rooms = new ModelRooms('room', true);

@ -0,0 +1,178 @@
class ModelSettings extends RocketChat.models._Base {
constructor() {
super(...arguments);
this.tryEnsureIndex({ 'blocked': 1 }, { sparse: 1 });
this.tryEnsureIndex({ 'hidden': 1 }, { sparse: 1 });
}
// FIND
findById(_id) {
const query = {_id};
return this.find(query);
}
findOneNotHiddenById(_id) {
const query = {
_id,
hidden: { $ne: true }
};
return this.findOne(query);
}
findByIds(_id = []) {
_id = [].concat(_id);
const query = {
_id: {
$in: _id
}
};
return this.find(query);
}
findByRole(role, options) {
const query = {role};
return this.find(query, options);
}
findPublic(options) {
const query = {public: true};
return this.find(query, options);
}
findNotHiddenPublic(ids = []) {
const filter = {
hidden: { $ne: true },
public: true
};
if (ids.length > 0) {
filter._id =
{$in: ids};
}
return this.find(filter, { fields: {_id: 1, value: 1} });
}
findNotHiddenPublicUpdatedAfter(updatedAt) {
const filter = {
hidden: { $ne: true },
public: true,
_updatedAt: {
$gt: updatedAt
}
};
return this.find(filter, { fields: {_id: 1, value: 1} });
}
findNotHiddenPrivate() {
return this.find({
hidden: { $ne: true },
public: { $ne: true }
});
}
findNotHidden(options) {
return this.find({ hidden: { $ne: true } }, options);
}
findNotHiddenUpdatedAfter(updatedAt) {
return this.find({
hidden: { $ne: true },
_updatedAt: {
$gt: updatedAt
}
});
}
// UPDATE
updateValueById(_id, value) {
const query = {
blocked: { $ne: true },
value: { $ne: value },
_id
};
const update = {
$set: {
value
}
};
return this.update(query, update);
}
updateValueAndEditorById(_id, value, editor) {
const query = {
blocked: { $ne: true },
value: { $ne: value },
_id
};
const update = {
$set: {
value,
editor
}
};
return this.update(query, update);
}
updateValueNotHiddenById(_id, value) {
const query = {
_id,
hidden: { $ne: true },
blocked: { $ne: true }
};
const update = {
$set: {
value
}
};
return this.update(query, update);
}
updateOptionsById(_id, options) {
const query = {
blocked: { $ne: true },
_id
};
const update = {$set: options};
return this.update(query, update);
}
// INSERT
createWithIdAndValue(_id, value) {
const record = {
_id,
value,
_createdAt: new Date
};
return this.insert(record);
}
// REMOVE
removeById(_id) {
const query = {
blocked: { $ne: true },
_id
};
return this.remove(query);
}
}
RocketChat.models.Settings = new ModelSettings('settings', true);

@ -0,0 +1,570 @@
class ModelSubscriptions extends RocketChat.models._Base {
constructor() {
super(...arguments);
this.tryEnsureIndex({ 'rid': 1, 'u._id': 1 }, { unique: 1 });
this.tryEnsureIndex({ 'rid': 1, 'alert': 1, 'u._id': 1 });
this.tryEnsureIndex({ 'rid': 1, 'roles': 1 });
this.tryEnsureIndex({ 'u._id': 1, 'name': 1, 't': 1 });
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({ 'ts': 1 });
this.tryEnsureIndex({ 'ls': 1 });
this.tryEnsureIndex({ 'audioNotification': 1 }, { sparse: 1 });
this.tryEnsureIndex({ 'desktopNotifications': 1 }, { sparse: 1 });
this.tryEnsureIndex({ 'mobilePushNotifications': 1 }, { sparse: 1 });
this.tryEnsureIndex({ 'emailNotifications': 1 }, { sparse: 1 });
this.tryEnsureIndex({ 'autoTranslate': 1 }, { sparse: 1 });
this.tryEnsureIndex({ 'autoTranslateLanguage': 1 }, { sparse: 1 });
this.cache.ensureIndex('rid', 'array');
this.cache.ensureIndex('u._id', 'array');
this.cache.ensureIndex('name', 'array');
this.cache.ensureIndex(['rid', 'u._id'], 'unique');
this.cache.ensureIndex(['name', 'u._id'], 'unique');
}
// FIND ONE
findOneByRoomIdAndUserId(roomId, userId) {
if (this.useCache) {
return this.cache.findByIndex('rid,u._id', [roomId, userId]).fetch();
}
const query = {
rid: roomId,
'u._id': userId
};
return this.findOne(query);
}
findOneByRoomNameAndUserId(roomName, userId) {
if (this.useCache) {
return this.cache.findByIndex('name,u._id', [roomName, userId]).fetch();
}
const query = {
name: roomName,
'u._id': userId
};
return this.findOne(query);
}
// FIND
findByUserId(userId, options) {
if (this.useCache) {
return this.cache.findByIndex('u._id', userId, options);
}
const query =
{'u._id': userId};
return this.find(query, options);
}
findByUserIdUpdatedAfter(userId, updatedAt, options) {
const query = {
'u._id': userId,
_updatedAt: {
$gt: updatedAt
}
};
return this.find(query, options);
}
// FIND
findByRoomIdAndRoles(roomId, roles, options) {
roles = [].concat(roles);
const query = {
'rid': roomId,
'roles': { $in: roles }
};
return this.find(query, options);
}
findByType(types, options) {
const query = {
t: {
$in: types
}
};
return this.find(query, options);
}
findByTypeAndUserId(type, userId, options) {
const query = {
t: type,
'u._id': userId
};
return this.find(query, options);
}
findByTypeNameAndUserId(type, name, userId, options) {
const query = {
t: type,
name,
'u._id': userId
};
return this.find(query, options);
}
findByRoomId(roomId, options) {
if (this.useCache) {
return this.cache.findByIndex('rid', roomId, options);
}
const query =
{rid: roomId};
return this.find(query, options);
}
findByRoomIdAndNotUserId(roomId, userId, options) {
const query = {
rid: roomId,
'u._id': {
$ne: userId
}
};
return this.find(query, options);
}
getLastSeen(options) {
if (options == null) { options = {}; }
const query = { ls: { $exists: 1 } };
options.sort = { ls: -1 };
options.limit = 1;
const [subscription] = this.find(query, options).fetch();
return subscription && subscription.ls;
}
findByRoomIdAndUserIds(roomId, userIds) {
const query = {
rid: roomId,
'u._id': {
$in: userIds
}
};
return this.find(query);
}
// UPDATE
archiveByRoomId(roomId) {
const query =
{rid: roomId};
const update = {
$set: {
alert: false,
open: false,
archived: true
}
};
return this.update(query, update, { multi: true });
}
unarchiveByRoomId(roomId) {
const query =
{rid: roomId};
const update = {
$set: {
alert: false,
open: true,
archived: false
}
};
return this.update(query, update, { multi: true });
}
hideByRoomIdAndUserId(roomId, userId) {
const query = {
rid: roomId,
'u._id': userId
};
const update = {
$set: {
alert: false,
open: false
}
};
return this.update(query, update);
}
openByRoomIdAndUserId(roomId, userId) {
const query = {
rid: roomId,
'u._id': userId
};
const update = {
$set: {
open: true
}
};
return this.update(query, update);
}
setAsReadByRoomIdAndUserId(roomId, userId) {
const query = {
rid: roomId,
'u._id': userId
};
const update = {
$set: {
open: true,
alert: false,
unread: 0,
ls: new Date
}
};
return this.update(query, update);
}
setAsUnreadByRoomIdAndUserId(roomId, userId, firstMessageUnreadTimestamp) {
const query = {
rid: roomId,
'u._id': userId
};
const update = {
$set: {
open: true,
alert: true,
ls: firstMessageUnreadTimestamp
}
};
return this.update(query, update);
}
setFavoriteByRoomIdAndUserId(roomId, userId, favorite) {
if (favorite == null) { favorite = true; }
const query = {
rid: roomId,
'u._id': userId
};
const update = {
$set: {
f: favorite
}
};
return this.update(query, update);
}
updateNameAndAlertByRoomId(roomId, name) {
const query =
{rid: roomId};
const update = {
$set: {
name,
alert: true
}
};
return this.update(query, update, { multi: true });
}
updateNameByRoomId(roomId, name) {
const query =
{rid: roomId};
const update = {
$set: {
name
}
};
return this.update(query, update, { multi: true });
}
setUserUsernameByUserId(userId, username) {
const query =
{'u._id': userId};
const update = {
$set: {
'u.username': username
}
};
return this.update(query, update, { multi: true });
}
setNameForDirectRoomsWithOldName(oldName, name) {
const query = {
name: oldName,
t: 'd'
};
const update = {
$set: {
name
}
};
return this.update(query, update, { multi: true });
}
incUnreadOfDirectForRoomIdExcludingUserId(roomId, userId, inc) {
if (inc == null) { inc = 1; }
const query = {
rid: roomId,
t: 'd',
'u._id': {
$ne: userId
}
};
const update = {
$set: {
alert: true,
open: true
},
$inc: {
unread: inc
}
};
return this.update(query, update, { multi: true });
}
incUnreadForRoomIdExcludingUserId(roomId, userId, inc) {
if (inc == null) { inc = 1; }
const query = {
rid: roomId,
'u._id': {
$ne: userId
}
};
const update = {
$set: {
alert: true,
open: true
},
$inc: {
unread: inc
}
};
return this.update(query, update, { multi: true });
}
incUnreadForRoomIdAndUserIds(roomId, userIds, inc) {
if (inc == null) { inc = 1; }
const query = {
rid: roomId,
'u._id': {
$in: userIds
}
};
const update = {
$set: {
alert: true,
open: true
},
$inc: {
unread: inc
}
};
return this.update(query, update, { multi: true });
}
setAlertForRoomIdExcludingUserId(roomId, userId) {
const query = {
rid: roomId,
'u._id': {
$ne: userId
},
$or: [
{ alert: { $ne: true } },
{ open: { $ne: true } }
]
};
const update = {
$set: {
alert: true,
open: true
}
};
return this.update(query, update, { multi: true });
}
setBlockedByRoomId(rid, blocked, blocker) {
const query = {
rid,
'u._id': blocked
};
const update = {
$set: {
blocked: true
}
};
const query2 = {
rid,
'u._id': blocker
};
const update2 = {
$set: {
blocker: true
}
};
return this.update(query, update) && this.update(query2, update2);
}
unsetBlockedByRoomId(rid, blocked, blocker) {
const query = {
rid,
'u._id': blocked
};
const update = {
$unset: {
blocked: 1
}
};
const query2 = {
rid,
'u._id': blocker
};
const update2 = {
$unset: {
blocker: 1
}
};
return this.update(query, update) && this.update(query2, update2);
}
updateTypeByRoomId(roomId, type) {
const query =
{rid: roomId};
const update = {
$set: {
t: type
}
};
return this.update(query, update, { multi: true });
}
addRoleById(_id, role) {
const query =
{_id};
const update = {
$addToSet: {
roles: role
}
};
return this.update(query, update);
}
removeRoleById(_id, role) {
const query =
{_id};
const update = {
$pull: {
roles: role
}
};
return this.update(query, update);
}
setArchivedByUsername(username, archived) {
const query = {
t: 'd',
name: username
};
const update = {
$set: {
archived
}
};
return this.update(query, update, { multi: true });
}
// INSERT
createWithRoomAndUser(room, user, extraData) {
const subscription = {
open: false,
alert: false,
unread: 0,
ts: room.ts,
rid: room._id,
name: room.name,
t: room.t,
u: {
_id: user._id,
username: user.username
}
};
_.extend(subscription, extraData);
return this.insert(subscription);
}
// REMOVE
removeByUserId(userId) {
const query =
{'u._id': userId};
return this.remove(query);
}
removeByRoomId(roomId) {
const query =
{rid: roomId};
return this.remove(query);
}
removeByRoomIdAndUserId(roomId, userId) {
const query = {
rid: roomId,
'u._id': userId
};
return this.remove(query);
}
}
RocketChat.models.Subscriptions = new ModelSubscriptions('subscription', true);

@ -0,0 +1,91 @@
RocketChat.models.Uploads = new class extends RocketChat.models._Base {
constructor() {
super('uploads');
this.tryEnsureIndex({ 'rid': 1 });
this.tryEnsureIndex({ 'uploadedAt': 1 });
}
findNotHiddenFilesOfRoom(roomId, limit) {
const fileQuery = {
rid: roomId,
complete: true,
uploading: false,
_hidden: {
$ne: true
}
};
const fileOptions = {
limit,
sort: {
uploadedAt: -1
},
fields: {
_id: 1,
userId: 1,
rid: 1,
name: 1,
description: 1,
type: 1,
url: 1,
uploadedAt: 1
}
};
return this.find(fileQuery, fileOptions);
}
insertFileInit(roomId, userId, store, file, extra) {
const fileData = {
rid: roomId,
userId,
store,
complete: false,
uploading: true,
progress: 0,
extension: s.strRightBack(file.name, '.'),
uploadedAt: new Date()
};
_.extend(fileData, file, extra);
if ((this.model.direct != null ? this.model.direct.insert : undefined) != null) {
file = this.model.direct.insert(fileData);
} else {
file = this.insert(fileData);
}
return file;
}
updateFileComplete(fileId, userId, file) {
let result;
if (!fileId) {
return;
}
const filter = {
_id: fileId,
userId
};
const update = {
$set: {
complete: true,
uploading: false,
progress: 1
}
};
update.$set = _.extend(file, update.$set);
if ((this.model.direct != null ? this.model.direct.insert : undefined) != null) {
result = this.model.direct.update(filter, update);
} else {
result = this.update(filter, update);
}
return result;
}
};

@ -0,0 +1,538 @@
class ModelUsers extends RocketChat.models._Base {
constructor() {
super(...arguments);
this.tryEnsureIndex({ 'roles': 1 }, { sparse: 1 });
this.tryEnsureIndex({ 'name': 1 });
this.tryEnsureIndex({ 'lastLogin': 1 });
this.tryEnsureIndex({ 'status': 1 });
this.tryEnsureIndex({ 'active': 1 }, { sparse: 1 });
this.tryEnsureIndex({ 'statusConnection': 1 }, { sparse: 1 });
this.tryEnsureIndex({ 'type': 1 });
this.cache.ensureIndex('username', 'unique');
}
findOneByImportId(_id, options) {
return this.findOne({ importIds: _id }, options);
}
findOneByUsername(username, options) {
const query = {username};
return this.findOne(query, options);
}
findOneByEmailAddress(emailAddress, options) {
const query = {'emails.address': new RegExp(`^${ s.escapeRegExp(emailAddress) }$`, 'i')};
return this.findOne(query, options);
}
findOneAdmin(admin, options) {
const query = {admin};
return this.findOne(query, options);
}
findOneByIdAndLoginToken(_id, token, options) {
const query = {
_id,
'services.resume.loginTokens.hashedToken' : Accounts._hashLoginToken(token)
};
return this.findOne(query, options);
}
// FIND
findById(userId) {
const query = {_id: userId};
return this.find(query);
}
findUsersNotOffline(options) {
const query = {
username: {
$exists: 1
},
status: {
$in: ['online', 'away', 'busy']
}
};
return this.find(query, options);
}
findByUsername(username, options) {
const query = {username};
return this.find(query, options);
}
findUsersByUsernamesWithHighlights(usernames, options) {
if (this.useCache) {
const result = {
fetch() {
return RocketChat.models.Users.getDynamicView('highlights').data().filter(record => usernames.indexOf(record.username) > -1);
},
count() {
return result.fetch().length;
},
forEach(fn) {
return result.fetch().forEach(fn);
}
};
return result;
}
const query = {
username: { $in: usernames },
'settings.preferences.highlights.0': {
$exists: true
}
};
return this.find(query, options);
}
findActiveByUsernameOrNameRegexWithExceptions(searchTerm, exceptions, options) {
if (exceptions == null) { exceptions = []; }
if (options == null) { options = {}; }
if (!_.isArray(exceptions)) {
exceptions = [ exceptions ];
}
const termRegex = new RegExp(s.escapeRegExp(searchTerm), 'i');
const query = {
$or: [{
username: termRegex
}, {
name: termRegex
}],
active: true,
type: {
$in: ['user', 'bot']
},
$and: [{
username: {
$exists: true
}
}, {
username: {
$nin: exceptions
}
}]
};
return this.find(query, options);
}
findByActiveUsersExcept(searchTerm, exceptions, options) {
if (exceptions == null) { exceptions = []; }
if (options == null) { options = {}; }
if (!_.isArray(exceptions)) {
exceptions = [ exceptions ];
}
const termRegex = new RegExp(s.escapeRegExp(searchTerm), 'i');
const query = {
$and: [
{
active: true,
$or: [
{
username: termRegex
},
{
name: termRegex
}
]
},
{
username: { $exists: true, $nin: exceptions }
}
]
};
return this.find(query, options);
}
findUsersByNameOrUsername(nameOrUsername, options) {
const query = {
username: {
$exists: 1
},
$or: [
{name: nameOrUsername},
{username: nameOrUsername}
],
type: {
$in: ['user']
}
};
return this.find(query, options);
}
findByUsernameNameOrEmailAddress(usernameNameOrEmailAddress, options) {
const query = {
$or: [
{name: usernameNameOrEmailAddress},
{username: usernameNameOrEmailAddress},
{'emails.address': usernameNameOrEmailAddress}
],
type: {
$in: ['user', 'bot']
}
};
return this.find(query, options);
}
findLDAPUsers(options) {
const query = {ldap: true};
return this.find(query, options);
}
findCrowdUsers(options) {
const query = {crowd: true};
return this.find(query, options);
}
getLastLogin(options) {
if (options == null) { options = {}; }
const query = { lastLogin: { $exists: 1 } };
options.sort = { lastLogin: -1 };
options.limit = 1;
const [user] = this.find(query, options).fetch();
return user && user.lastLogin;
}
findUsersByUsernames(usernames, options) {
const query = {
username: {
$in: usernames
}
};
return this.find(query, options);
}
// UPDATE
addImportIds(_id, importIds) {
importIds = [].concat(importIds);
const query = {_id};
const update = {
$addToSet: {
importIds: {
$each: importIds
}
}
};
return this.update(query, update);
}
updateLastLoginById(_id) {
const update = {
$set: {
lastLogin: new Date
}
};
return this.update(_id, update);
}
setServiceId(_id, serviceName, serviceId) {
const update =
{$set: {}};
const serviceIdKey = `services.${ serviceName }.id`;
update.$set[serviceIdKey] = serviceId;
return this.update(_id, update);
}
setUsername(_id, username) {
const update =
{$set: {username}};
return this.update(_id, update);
}
setEmail(_id, email) {
const update = {
$set: {
emails: [{
address: email,
verified: false
}
]
}
};
return this.update(_id, update);
}
setEmailVerified(_id, email) {
const query = {
_id,
emails: {
$elemMatch: {
address: email,
verified: false
}
}
};
const update = {
$set: {
'emails.$.verified': true
}
};
return this.update(query, update);
}
setName(_id, name) {
const update = {
$set: {
name
}
};
return this.update(_id, update);
}
setCustomFields(_id, fields) {
const values = {};
Object.keys(fields).reduce(key => {
const value = fields[key];
values[`customFields.${ key }`] = value;
});
const update = {$set: values};
return this.update(_id, update);
}
setAvatarOrigin(_id, origin) {
const update = {
$set: {
avatarOrigin: origin
}
};
return this.update(_id, update);
}
unsetAvatarOrigin(_id) {
const update = {
$unset: {
avatarOrigin: 1
}
};
return this.update(_id, update);
}
setUserActive(_id, active) {
if (active == null) { active = true; }
const update = {
$set: {
active
}
};
return this.update(_id, update);
}
setAllUsersActive(active) {
const update = {
$set: {
active
}
};
return this.update({}, update, { multi: true });
}
unsetLoginTokens(_id) {
const update = {
$set: {
'services.resume.loginTokens' : []
}
};
return this.update(_id, update);
}
unsetRequirePasswordChange(_id) {
const update = {
$unset: {
'requirePasswordChange' : true,
'requirePasswordChangeReason' : true
}
};
return this.update(_id, update);
}
resetPasswordAndSetRequirePasswordChange(_id, requirePasswordChange, requirePasswordChangeReason) {
const update = {
$unset: {
'services.password': 1
},
$set: {
requirePasswordChange,
requirePasswordChangeReason
}
};
return this.update(_id, update);
}
setLanguage(_id, language) {
const update = {
$set: {
language
}
};
return this.update(_id, update);
}
setProfile(_id, profile) {
const update = {
$set: {
'settings.profile': profile
}
};
return this.update(_id, update);
}
setPreferences(_id, preferences) {
const update = {
$set: {
'settings.preferences': preferences
}
};
return this.update(_id, update);
}
setUtcOffset(_id, utcOffset) {
const query = {
_id,
utcOffset: {
$ne: utcOffset
}
};
const update = {
$set: {
utcOffset
}
};
return this.update(query, update);
}
saveUserById(_id, data) {
const setData = {};
const unsetData = {};
if (data.name != null) {
if (!_.isEmpty(s.trim(data.name))) {
setData.name = s.trim(data.name);
} else {
unsetData.name = 1;
}
}
if (data.email != null) {
if (!_.isEmpty(s.trim(data.email))) {
setData.emails = [{address: s.trim(data.email)}];
} else {
unsetData.emails = 1;
}
}
if (data.phone != null) {
if (!_.isEmpty(s.trim(data.phone))) {
setData.phone = [{phoneNumber: s.trim(data.phone)}];
} else {
unsetData.phone = 1;
}
}
const update = {};
if (!_.isEmpty(setData)) {
update.$set = setData;
}
if (!_.isEmpty(unsetData)) {
update.$unset = unsetData;
}
if (_.isEmpty(update)) {
return true;
}
return this.update({ _id }, update);
}
// INSERT
create(data) {
const user = {
createdAt: new Date,
avatarOrigin: 'none'
};
_.extend(user, data);
return this.insert(user);
}
// REMOVE
removeById(_id) {
return this.remove(_id);
}
/*
Find users to send a message by email if:
- he is not online
- has a verified email
- has not disabled email notifications
- `active` is equal to true (false means they were deactivated and can't login)
*/
getUsersToSendOfflineEmail(usersIds) {
const query = {
_id: {
$in: usersIds
},
active: true,
status: 'offline',
statusConnection: {
$ne: 'online'
},
'emails.verified': true
};
return this.find(query, { fields: { name: 1, username: 1, emails: 1, 'settings.preferences.emailNotificationMode': 1 } });
}
}
RocketChat.models.Users = new ModelUsers(Meteor.users, true);
Loading…
Cancel
Save