From 8bda497b41f3e7f8a2db678e8e0f5f4184af5263 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 15 Dec 2015 11:24:29 +0200 Subject: [PATCH 01/27] upgrade script for chats --- src/upgrade.js | 94 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 3 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index ecbfcfe6dd..4b2f78e56c 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -10,7 +10,7 @@ var db = require('./database'), schemaDate, thisSchemaDate, // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema - latestSchema = Date.UTC(2015, 10, 6); + latestSchema = Date.UTC(2015, 11, 15); Upgrade.check = function(callback) { db.get('schemaDate', function(err, value) { @@ -122,7 +122,7 @@ Upgrade.upgrade = function(callback) { db.setObjectField('user:' + uid, 'picture', '', next); }, function (next) { - db.deleteObjectField('user:' + uid, 'gravatarpicture', next); + db.deleteObjectField('user:' + uid, 'gravatarpicture', next); } ], next); } else { @@ -143,8 +143,96 @@ Upgrade.upgrade = function(callback) { winston.info('[2015/11/06] Gravatar removal skipped'); next(); } - } + }, + function(next) { + thisSchemaDate = Date.UTC(2015, 11, 15); + + if (schemaDate < thisSchemaDate) { + updatesMade = true; + winston.info('[2015/12/15] Upgrading chats'); + + db.getObjectFields('global', ['nextMid', 'nextChatRoomId'], function(err, globalData) { + if (err) { + return next(err); + } + + var rooms = {}; + var roomId = globalData.nextChatRoomId || 1; + var currentMid = 1; + + async.whilst(function() { + return currentMid < globalData.nextMid; + }, function(next) { + db.getObject('message:' + currentMid, function(err, message) { + function addMessageToUids(roomId, callback) { + async.parallel([ + function(next) { + db.sortedSetAdd('uid:' + message.fromuid + ':chat:room:' + roomId + ':mids', msgTime, currentMid, next); + }, + function(next) { + db.sortedSetAdd('uid:' + message.touid + ':chat:room:' + roomId + ':mids', msgTime, currentMid, next); + } + ], callback); + } + + if (err || !message) { + winston.info('skipping chat message ', currentMid); + currentMid ++; + return next(err); + } + + var pairID = [parseInt(message.fromuid, 10), parseInt(message.touid, 10)].sort().join(':'); + var msgTime = parseInt(message.timestamp, 10); + + if (rooms[pairID]) { + winston.info('adding message ' + currentMid + ' to existing roomID ' + roomId); + addMessageToUids(rooms[pairID], function(err) { + if (err) { + return next(err); + } + currentMid ++; + next(); + }); + } else { + winston.info('adding message ' + currentMid + ' to new roomID ' + roomId); + async.parallel([ + function(next) { + db.sortedSetAdd('uid:' + message.fromuid + ':chat:rooms', msgTime, roomId, next); + }, + function(next) { + db.sortedSetAdd('uid:' + message.touid + ':chat:rooms', msgTime, roomId, next); + }, + function(next) { + db.sortedSetAdd('chat:room:' + roomId + ':uids', [msgTime, msgTime + 1], [message.fromuid, message.touid], next); + }, + function(next) { + addMessageToUids(roomId, next); + } + ], function(err) { + if (err) { + return next(err); + } + rooms[pairID] = roomId; + roomId ++; + currentMid ++; + db.setObjectField('global', 'nextChatRoomId', roomId, next); + }); + } + }); + }, function(err) { + if (err) { + return next(err); + } + winston.info('[2015/12/15] Chats upgrade done!'); + Upgrade.update(thisSchemaDate, next); + }); + }); + } else { + winston.info('[2015/12/15] Chats upgrade skipped!'); + next(); + } + } // Add new schema updates here // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 24!!! ], function(err) { From e2e547db45601064927a635cf4eda32fd6e5e68f Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 15 Dec 2015 14:10:32 +0200 Subject: [PATCH 02/27] first part of messaging refactor --- src/messaging.js | 214 ++------------------------------------- src/messaging/create.js | 134 ++++++++++++++++++++++++ src/messaging/delete.js | 27 +++++ src/messaging/edit.js | 79 +++++++++++++++ src/messaging/rooms.js | 46 +++++++++ src/messaging/unread.js | 44 ++++++++ src/socket.io/modules.js | 6 +- 7 files changed, 344 insertions(+), 206 deletions(-) create mode 100644 src/messaging/create.js create mode 100644 src/messaging/delete.js create mode 100644 src/messaging/edit.js create mode 100644 src/messaging/rooms.js create mode 100644 src/messaging/unread.js diff --git a/src/messaging.js b/src/messaging.js index bff12313ba..a3d6d58fd0 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -16,6 +16,13 @@ var db = require('./database'), sockets = require('./socket.io'); (function(Messaging) { + + require('./create')(Messaging); + require('./delete')(Messaging); + require('./edit')(Messaging); + require('./rooms')(Messaging); + require('./unread')(Messaging); + Messaging.notifyQueue = {}; // Only used to notify a user of a new chat message, see Messaging.notifyUser var terms = { @@ -29,140 +36,6 @@ var db = require('./database'), return [fromuid, touid].sort(); } - Messaging.addMessage = function(fromuid, touid, content, timestamp, callback) { - if (typeof timestamp === 'function') { - callback = timestamp; - timestamp = Date.now(); - } else { - timestamp = timestamp || Date.now(); - } - - if (!content) { - return callback(new Error('[[error:invalid-chat-message]]')); - } - - if (content.length > (meta.config.maximumChatMessageLength || 1000)) { - return callback(new Error('[[error:chat-message-too-long]]')); - } - - var uids = sortUids(fromuid, touid); - - db.incrObjectField('global', 'nextMid', function(err, mid) { - if (err) { - return callback(err); - } - var message = { - content: content, - timestamp: timestamp, - fromuid: fromuid, - touid: touid - }; - - async.waterfall([ - function(next) { - plugins.fireHook('filter:messaging.save', message, next); - }, - function(message, next) { - db.setObject('message:' + mid, message, next); - } - ], function(err) { - if (err) { - return callback(err); - } - - async.parallel([ - async.apply(db.sortedSetAdd, 'messages:uid:' + uids[0] + ':to:' + uids[1], timestamp, mid), - async.apply(Messaging.updateChatTime, fromuid, touid), - async.apply(Messaging.updateChatTime, touid, fromuid), - async.apply(Messaging.markRead, fromuid, touid), - async.apply(Messaging.markUnread, touid, fromuid), - ], function(err) { - if (err) { - return callback(err); - } - - async.waterfall([ - function(next) { - getMessages([mid], fromuid, touid, true, next); - }, - function(messages, next) { - Messaging.isNewSet(fromuid, touid, mid, function(err, isNewSet) { - if (err) { - return next(err); - } - - if (!messages || !messages[0]) { - return next(null, null); - } - - messages[0].newSet = isNewSet; - messages[0].mid = mid; - next(null, messages[0]); - }); - } - ], callback); - }); - }); - }); - }; - - Messaging.editMessage = function(mid, content, callback) { - async.series([ - function(next) { - // Verify that the message actually changed - Messaging.getMessageField(mid, 'content', function(err, raw) { - if (raw === content) { - // No dice. - return callback(); - } - - next(); - }); - }, - async.apply(Messaging.setMessageFields, mid, { - content: content, - edited: Date.now() - }), - function(next) { - Messaging.getMessageFields(mid, ['fromuid', 'touid'], function(err, data) { - getMessages([mid], data.fromuid, data.touid, true, function(err, messages) { - sockets.in('uid_' + data.fromuid).emit('event:chats.edit', { - messages: messages - }); - sockets.in('uid_' + data.touid).emit('event:chats.edit', { - messages: messages - }); - next(); - }); - }); - } - ], callback); - }; - - Messaging.deleteMessage = function(mid, callback) { - var uids = []; - async.series([ - function(next) { - db.getObject('message:' + mid, function(err, messageObj) { - messageObj.fromuid = parseInt(messageObj.fromuid, 10); - messageObj.touid = parseInt(messageObj.touid, 10); - uids.push(messageObj.fromuid, messageObj.touid); - uids.sort(function(a, b) { - return a > b ? 1 : -1; - }); - next(); - }); - }, - function(next) { - next(); - }, - function(next) { - db.sortedSetRemove('messages:uid:' + uids[0] + ':to:' + uids[1], mid, next); - }, - async.apply(db.delete, 'message:' + mid) - ], callback); - }; - Messaging.getMessageField = function(mid, field, callback) { Messaging.getMessageFields(mid, [field], function(err, fields) { callback(err, fields[field]); @@ -208,7 +81,7 @@ var db = require('./database'), mids.reverse(); - getMessages(mids, fromuid, touid, isNew, callback); + Messaging.getMessagesData(mids, fromuid, touid, isNew, callback); }); if (markRead) { @@ -222,7 +95,7 @@ var db = require('./database'), } }; - function getMessages(mids, fromuid, touid, isNew, callback) { + Messaging.getMessagesData = function(mids, fromuid, touid, isNew, callback) { user.getUsersFields([fromuid, touid], ['uid', 'username', 'userslug', 'picture', 'status'], function(err, userData) { if(err) { return callback(err); @@ -279,7 +152,7 @@ var db = require('./database'), next(undefined, messages); } else { // For single messages, we don't know the context, so look up the previous message and compare - var uids = [fromuid, touid].sort(function(a, b) { return a > b ? 1 : -1 }); + var uids = [fromuid, touid].sort(function(a, b) { return a > b ? 1 : -1; }); var key = 'messages:uid:' + uids[0] + ':to:' + uids[1]; async.waterfall([ async.apply(db.sortedSetRank, key, messages[0].messageId), @@ -314,7 +187,7 @@ var db = require('./database'), } ], callback); }); - } + }; Messaging.parse = function (message, fromuid, myuid, toUserData, myUserData, isNew, callback) { plugins.fireHook('filter:parse.raw', message, function(err, parsed) { @@ -369,10 +242,6 @@ var db = require('./database'), ], callback); }; - Messaging.updateChatTime = function(uid, toUid, callback) { - callback = callback || function() {}; - db.sortedSetAdd('uid:' + uid + ':chats', Date.now(), toUid, callback); - }; Messaging.getRecentChats = function(uid, start, stop, callback) { db.getSortedSetRevRange('uid:' + uid + ':chats', start, stop, function(err, uids) { @@ -424,36 +293,7 @@ var db = require('./database'), }); }; - Messaging.getUnreadCount = function(uid, callback) { - db.sortedSetCard('uid:' + uid + ':chats:unread', callback); - }; - Messaging.pushUnreadCount = function(uid) { - Messaging.getUnreadCount(uid, function(err, unreadCount) { - if (err) { - return; - } - sockets.in('uid_' + uid).emit('event:unread.updateChatCount', unreadCount); - }); - }; - - Messaging.markRead = function(uid, toUid, callback) { - db.sortedSetRemove('uid:' + uid + ':chats:unread', toUid, callback); - }; - - Messaging.markUnread = function(uid, toUid, callback) { - async.waterfall([ - function (next) { - user.exists(toUid, next); - }, - function (exists, next) { - if (!exists) { - return next(new Error('[[error:no-user]]')); - } - db.sortedSetAdd('uid:' + uid + ':chats:unread', Date.now(), toUid, next); - } - ], callback); - }; Messaging.notifyUser = function(fromuid, touid, messageObj) { // Immediate notifications @@ -534,38 +374,6 @@ var db = require('./database'), ], callback); }; - Messaging.canEdit = function(messageId, uid, callback) { - if (parseInt(meta.config.disableChat) === 1) { - return callback(null, false); - } - - async.waterfall([ - function (next) { - user.getUserFields(uid, ['banned', 'email:confirmed'], next); - }, - function (userData, next) { - if (parseInt(userData.banned, 10) === 1) { - return callback(null, false); - } - - if (parseInt(meta.config.requireEmailConfirmation, 10) === 1 && parseInt(userData['email:confirmed'], 10) !== 1) { - return callback(null, false); - } - - Messaging.getMessageField(messageId, 'fromuid', next); - }, - function(fromUid, next) { - if (parseInt(fromUid, 10) === parseInt(uid, 10)) { - return callback(null, true); - } - - user.isAdministrator(uid, next); - }, - function(isAdmin, next) { - next(null, isAdmin); - } - ], callback); - }; function sendNotifications(fromuid, touid, messageObj, callback) { user.isOnline(touid, function(err, isOnline) { diff --git a/src/messaging/create.js b/src/messaging/create.js new file mode 100644 index 0000000000..b14b8d9868 --- /dev/null +++ b/src/messaging/create.js @@ -0,0 +1,134 @@ +'use strict'; + +var async = require('async'); + +var meta = require('../meta'); +var plugins = require('../plugins'); +var db = require('../database'); + + +module.exports = function(Messaging) { + + + Messaging.newMessage = function(fromuid, toUids, content, timestamp, callback) { + var roomId; + async.waterfall([ + function (next) { + Messaging.checkContent(content, next); + }, + function (next) { + db.incrObjectField('global', 'nextChatRoomId', next); + }, + function (_roomId, next) { + roomId = _roomId; + db.sortedSetAdd('chat:room:' + roomId + ':uids', timestamp, fromuid, next); + }, + function (next) { + Messaging.addUsersToRoom(fromuid, toUids, roomId, next); + }, + function (next) { + Messaging.sendMessage(fromuid, roomId, content, timestamp, next); + } + ], callback); + }; + + Messaging.sendMessage = function(fromuid, roomId, content, timestamp, callback) { + async.waterfall([ + function (next) { + Messaging.checkContent(content, next); + }, + function (next) { + Messaging.roomExists(roomId, next); + }, + function (exists, next) { + if (!exists) { + return next(new Error('[[error:chat-room-does-not-exist]]')); + } + Messaging.addMessage(fromuid, roomId, content, timestamp, next); + } + ], callback); + }; + + Messaging.checkContent = function(content, callback) { + if (!content) { + return callback(new Error('[[error:invalid-chat-message]]')); + } + + if (content.length > (meta.config.maximumChatMessageLength || 1000)) { + return callback(new Error('[[error:chat-message-too-long]]')); + } + }; + + Messaging.addMessage = function(fromuid, roomId, content, timestamp, callback) { + var mid; + var message; + async.waterfall([ + function (next) { + Messaging.checkContent(content, next); + }, + function (next) { + db.incrObjectField('global', 'nextMid', next); + }, + function (_mid, next) { + mid = _mid; + message = { + content: content, + timestamp: timestamp, + fromuid: fromuid, + roomId: roomId + }; + + plugins.fireHook('filter:messaging.save', message, next); + }, + function (message, next) { + db.setObject('message:' + mid, message, next); + }, + function (next) { + db.getSortedSetRange('chat:room:' + roomId + ':uids', 0, -1, next); + }, + function (uids, next) { + async.parallel([ + async.apply(Messaging.updateChatTime, roomId, uids, timestamp), + async.apply(Messaging.addMessageToUsers, roomId, uids, mid, timestamp), + async.apply(Messaging.markRead, fromuid, roomId), + async.apply(Messaging.markUnread, uids, roomId) + ], next); + }, + function (results, next) { + getMessages([mid], fromuid, touid, true, next); + }, + function (messages, next) { + Messaging.isNewSet(fromuid, touid, mid, next); + }, + function (isNewSet, next) { + if (!messages || !messages[0]) { + return next(null, null); + } + + messages[0].newSet = isNewSet; + messages[0].mid = mid; + next(null, messages[0]); + } + ], callback); + }; + + Messaging.updateChatTime = function(roomId, uids, timestamp, callback) { + if (!uids.length) { + return callback(); + } + var keys = uids.map(function(uid) { + return 'uid:' + uid + ':chat:rooms'; + }); + db.sortedSetsAdd(keys, timestamp, roomId, next); + }; + + Messaging.addMessageToUsers = function(roomId, uids, mid, timestamp, callback) { + if (!uids.length) { + return callback(); + } + var keys = uids.map(function(uid) { + return 'uid:' + uid + ':chat:room:' + roomId + ':mids'; + }); + db.sortedSetsAdd(keys, timestamp, mid, callback); + }; +}; \ No newline at end of file diff --git a/src/messaging/delete.js b/src/messaging/delete.js new file mode 100644 index 0000000000..518ab9fafb --- /dev/null +++ b/src/messaging/delete.js @@ -0,0 +1,27 @@ +'use strict'; + +var async = require('async'); +var db = require('../database'); + +module.exports = function(Messaging) { + + Messaging.deleteMessage = function(mid, roomId, callback) { + async.waterfall([ + function (next) { + Messaging.getUidsInRoom(roomId, 0, -1, next); + }, + function (uids, next) { + if (!uids.length) { + return next(); + } + var keys = uids.map(function(uid) { + return 'uid:' + uid + ':chat:room:' + roomId + 'mids'; + }); + db.sortedSetsRemove(keys, roomId, next); + }, + function(next) { + db.delete('message:' + mid, next); + } + ], callback); + }; +}; \ No newline at end of file diff --git a/src/messaging/edit.js b/src/messaging/edit.js new file mode 100644 index 0000000000..7da95f7320 --- /dev/null +++ b/src/messaging/edit.js @@ -0,0 +1,79 @@ +'use strict'; + +var async = require('async'); + +var meta = require('../meta'); +var user = require('../user'); + +var sockets = require('../socket.io'); + + +module.exports = function(Messaging) { + + Messaging.editMessage = function(mid, content, callback) { + async.series([ + function(next) { + // Verify that the message actually changed + Messaging.getMessageField(mid, 'content', function(err, raw) { + if (raw === content) { + // No dice. + return callback(); + } + + next(); + }); + }, + async.apply(Messaging.setMessageFields, mid, { + content: content, + edited: Date.now() + }), + function(next) { + Messaging.getMessageFields(mid, ['fromuid', 'touid'], function(err, data) { + Messaging.getMessagesData([mid], data.fromuid, data.touid, true, function(err, messages) { + sockets.in('uid_' + data.fromuid).emit('event:chats.edit', { + messages: messages + }); + sockets.in('uid_' + data.touid).emit('event:chats.edit', { + messages: messages + }); + next(); + }); + }); + } + ], callback); + }; + + Messaging.canEdit = function(messageId, uid, callback) { + if (parseInt(meta.config.disableChat) === 1) { + return callback(null, false); + } + + async.waterfall([ + function (next) { + user.getUserFields(uid, ['banned', 'email:confirmed'], next); + }, + function (userData, next) { + if (parseInt(userData.banned, 10) === 1) { + return callback(null, false); + } + + if (parseInt(meta.config.requireEmailConfirmation, 10) === 1 && parseInt(userData['email:confirmed'], 10) !== 1) { + return callback(null, false); + } + + Messaging.getMessageField(messageId, 'fromuid', next); + }, + function(fromUid, next) { + if (parseInt(fromUid, 10) === parseInt(uid, 10)) { + return callback(null, true); + } + + user.isAdministrator(uid, next); + }, + function(isAdmin, next) { + next(null, isAdmin); + } + ], callback); + }; + +}; \ No newline at end of file diff --git a/src/messaging/rooms.js b/src/messaging/rooms.js new file mode 100644 index 0000000000..e94a696b5c --- /dev/null +++ b/src/messaging/rooms.js @@ -0,0 +1,46 @@ +'use strict'; + +var async = require('async'); + +var db = require('../database'); + +module.exports = function(Messaging) { + + Messaging.roomExists = function(roomId, callback) { + db.exists('chat:room:' + roomId + ':uids', callback); + }; + + Messaging.isRoomOwner = function(uid, roomId, callback) { + db.getSortedSetRange('chat:room:' + roomId + ':uids', 0, 0, function(err, uids) { + if (err) { + return callback(err); + } + if (!Array.isArray(uids) || !uids.length) { + return callback(null, false); + } + callback(null, parseInt(uids[0], 10) === parseInt(uid, 10)); + }); + }; + + Messaging.addUsersToRoom = function(fromuid, toUids, roomId, callback) { + async.waterfall([ + function (next) { + Messaging.isRoomOwner(fromuid, roomId, next); + }, + function (isOwner, next) { + if (!isOwner) { + return next(new Error('[[error:cant-add-users-to-chat-room]]')); + } + var now = Date.now(); + var timestamps = toUids.map(function() { + return now; + }); + db.sortedSetAdd('chat:room:' + roomId + ':uids', timestamps, toUids, next); + } + ], callback); + }; + + Messaging.getUidsInRoom = function(roomId, start, stop, callback) { + db.getSortedSetRange('chat:room:' + roomId + ':uids', start, stop, callback); + }; +}; \ No newline at end of file diff --git a/src/messaging/unread.js b/src/messaging/unread.js new file mode 100644 index 0000000000..6933d9c1fb --- /dev/null +++ b/src/messaging/unread.js @@ -0,0 +1,44 @@ +'use strict'; + +var async = require('async'); + +var db = require('../database'); +var sockets = require('../socket.io'); + +module.exports = function(Messaging) { + + Messaging.getUnreadCount = function(uid, callback) { + db.sortedSetCard('uid:' + uid + ':chat:rooms:unread', callback); + }; + + Messaging.pushUnreadCount = function(uid) { + Messaging.getUnreadCount(uid, function(err, unreadCount) { + if (err) { + return; + } + sockets.in('uid_' + uid).emit('event:unread.updateChatCount', unreadCount); + }); + }; + + Messaging.markRead = function(uid, roomId, callback) { + db.sortedSetRemove('uid:' + uid + ':chat:rooms:unread', roomId, callback); + }; + + Messaging.markUnread = function(uids, roomId, callback) { + async.waterfall([ + function (next) { + Messaging.roomExists(roomId, next); + }, + function (exists, next) { + if (!exists) { + return next(new Error('[[error:chat-room-does-not-exist]]')); + } + var keys = uids.map(function(uid) { + return 'uid:' + uid + ':chat:rooms:unread'; + }); + db.sortedSetAdd(keys, Date.now(), roomId, next); + } + ], callback); + }; + +}; \ No newline at end of file diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index 42ac4092d2..701975dcce 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -61,7 +61,7 @@ SocketModules.chats.send = function(socket, data, callback) { return callback(err || new Error('[[error:chat-restricted]]')); } - Messaging.addMessage(socket.uid, touid, data.message, function(err, message) { + Messaging.addMessage(socket.uid, touid, data.message, now, function(err, message) { if (err) { return callback(err); } @@ -88,13 +88,13 @@ SocketModules.chats.edit = function(socket, data, callback) { }; SocketModules.chats.delete = function(socket, data, callback) { - if (!data) { + if (!data || !data.roomId || !data.messageId) { return callback(new Error('[[error:invalid-data]]')); } Messaging.canEdit(data.messageId, socket.uid, function(err, allowed) { if (allowed) { - Messaging.deleteMessage(data.messageId, callback); + Messaging.deleteMessage(data.messageId, data.roomId, callback); } }); } From 58d05f7c79562cdab397801011759f0e01e1fc91 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 15 Dec 2015 17:50:30 +0200 Subject: [PATCH 03/27] recent chat list with rooms --- public/src/modules/chat.js | 27 ++-- src/messaging.js | 276 ++++++++++++++++++++----------------- src/messaging/create.js | 4 +- src/messaging/rooms.js | 1 + src/routes/debug.js | 7 +- src/socket.io/modules.js | 7 +- 6 files changed, 175 insertions(+), 147 deletions(-) diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index 14bf5982c5..b294107955 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -1,5 +1,5 @@ "use strict"; -/* globals app, config, define, socket, templates, utils, ajaxify */ +/* globals app, define, socket, templates, utils, ajaxify */ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'translator'], function(components, taskbar, S, sounds, Chats, translator) { @@ -90,15 +90,16 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra module.loadChatsDropdown = function(chatsListEl) { var dropdownEl; - socket.emit('modules.chats.getRecentChats', {after: 0}, function(err, chats) { + socket.emit('modules.chats.getRecentChats', {after: 0}, function(err, data) { if (err) { return app.alertError(err.message); } - chats = chats.users; + + var rooms = data.rooms; chatsListEl.empty(); - if (!chats.length) { + if (!rooms.length) { translator.translate('[[modules:chat.no_active]]', function(str) { $('
  • ') .addClass('no_active') @@ -108,17 +109,23 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra return; } - chats.forEach(function(userObj) { - dropdownEl = $('
  • ') - .attr('data-uid', userObj.uid) - .html(''+ + rooms.forEach(function(roomObj) { + function createUserImage(userObj) { + return '' + (userObj.picture ? '' : '
    ' + userObj['icon:text'] + '
    ') + ' ' + - userObj.username + '
    ') + userObj.username + ''; + } + + dropdownEl = $('
  • ') + .attr('data-roomId', roomObj.roomId) .appendTo(chatsListEl); + roomObj.users.forEach(function(userObj) { + dropdownEl.append(createUserImage(userObj)); + }); dropdownEl.click(function() { if (!ajaxify.currentPage.match(/^chats\//)) { @@ -229,7 +236,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra chatModal.find('.modal-header').on('dblclick', gotoChats); chatModal.find('button[data-action="maximize"]').on('click', gotoChats); - chatModal.on('click', function(e) { + chatModal.on('click', function() { module.bringModalToTop(chatModal); if (!dragged) { diff --git a/src/messaging.js b/src/messaging.js index a3d6d58fd0..63b64beca2 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -1,27 +1,28 @@ 'use strict'; -var db = require('./database'), - async = require('async'), - nconf = require('nconf'), + +var async = require('async'), winston = require('winston'), S = require('string'), + nconf = require('nconf'), + db = require('./database'), user = require('./user'), plugins = require('./plugins'), meta = require('./meta'), + emailer = require('./emailer'), utils = require('../public/src/utils'), notifications = require('./notifications'), userNotifications = require('./user/notifications'), - emailer = require('./emailer'), sockets = require('./socket.io'); (function(Messaging) { - require('./create')(Messaging); - require('./delete')(Messaging); - require('./edit')(Messaging); - require('./rooms')(Messaging); - require('./unread')(Messaging); + require('./messaging/create')(Messaging); + require('./messaging/delete')(Messaging); + require('./messaging/edit')(Messaging); + require('./messaging/rooms')(Messaging); + require('./messaging/unread')(Messaging); Messaging.notifyQueue = {}; // Only used to notify a user of a new chat message, see Messaging.notifyUser @@ -32,13 +33,9 @@ var db = require('./database'), threemonths: 7776000000 }; - function sortUids(fromuid, touid) { - return [fromuid, touid].sort(); - } - Messaging.getMessageField = function(mid, field, callback) { Messaging.getMessageFields(mid, [field], function(err, fields) { - callback(err, fields[field]); + callback(err, fields ? fields[field] : null); }); }; @@ -55,22 +52,21 @@ var db = require('./database'), }; Messaging.getMessages = function(params, callback) { - var fromuid = params.fromuid, - touid = params.touid, + var uid = params.uid, + roomId = params.roomId, since = params.since, isNew = params.isNew, count = params.count || parseInt(meta.config.chatMessageInboxSize, 10) || 250, markRead = params.markRead || true; - var uids = sortUids(fromuid, touid), - min = params.count ? 0 : Date.now() - (terms[since] || terms.day); + var min = params.count ? 0 : Date.now() - (terms[since] || terms.day); if (since === 'recent') { count = 49; min = 0; } - db.getSortedSetRevRangeByScore('messages:uid:' + uids[0] + ':to:' + uids[1], 0, count, '+inf', min, function(err, mids) { + db.getSortedSetRevRangeByScore('uid:' + uid + ':chat:room:' + roomId + ':mids', 0, count, '+inf', min, function(err, mids) { if (err) { return callback(err); } @@ -81,115 +77,122 @@ var db = require('./database'), mids.reverse(); - Messaging.getMessagesData(mids, fromuid, touid, isNew, callback); + Messaging.getMessagesData(mids, uid, roomId, isNew, callback); }); if (markRead) { - notifications.markRead('chat_' + touid + '_' + fromuid, fromuid, function(err) { + notifications.markRead('chat_' + roomId + '_' + uid, uid, function(err) { if (err) { winston.error('[messaging] Could not mark notifications related to this chat as read: ' + err.message); } - userNotifications.pushCount(fromuid); + userNotifications.pushCount(uid); }); } }; - Messaging.getMessagesData = function(mids, fromuid, touid, isNew, callback) { - user.getUsersFields([fromuid, touid], ['uid', 'username', 'userslug', 'picture', 'status'], function(err, userData) { - if(err) { - return callback(err); - } + Messaging.getMessagesData = function(mids, uid, roomId, isNew, callback) { - var keys = mids.map(function(mid) { - return 'message:' + mid; - }); + var keys = mids.map(function(mid) { + return 'message:' + mid; + }); - async.waterfall([ - async.apply(db.getObjects, keys), - function(messages, next) { - messages = messages.map(function(msg, idx) { - if (msg) { - msg.messageId = parseInt(mids[idx], 10); - } - return msg; - }).filter(Boolean); - async.map(messages, function(message, next) { - var self = parseInt(message.fromuid, 10) === parseInt(fromuid, 10); - message.fromUser = self ? userData[0] : userData[1]; - message.toUser = self ? userData[1] : userData[0]; - message.timestampISO = utils.toISOString(message.timestamp); - message.self = self ? 1 : 0; - message.newSet = false; - - if (message.hasOwnProperty('edited')) { - message.editedISO = new Date(parseInt(message.edited, 10)).toISOString(); - } + var messages; - Messaging.parse(message.content, message.fromuid, fromuid, userData[1], userData[0], isNew, function(result) { - message.content = result; - message.cleanedContent = S(result).stripTags().decodeHTMLEntities().s; - next(null, message); - }); - }, next); - }, - function(messages, next) { - if (messages.length > 1) { - // Add a spacer in between messages with time gaps between them - messages = messages.map(function(message, index) { - // Compare timestamps with the previous message, and check if a spacer needs to be added - if (index > 0 && parseInt(message.timestamp, 10) > parseInt(messages[index-1].timestamp, 10) + (1000*60*5)) { - // If it's been 5 minutes, this is a new set of messages - message.newSet = true; - } else if (index > 0 && message.fromuid !== messages[index-1].fromuid) { - // If the previous message was from the other person, this is also a new set - message.newSet = true; - } + async.waterfall([ + function (next) { + db.getObjects(keys, next); + }, + function (_messages, next) { + messages = _messages.map(function(msg, idx) { + if (msg) { + msg.messageId = parseInt(mids[idx], 10); + } + return msg; + }).filter(Boolean); - return message; - }); + var uids = messages.map(function(msg) { + return msg && msg.fromuid; + }); - next(undefined, messages); - } else { - // For single messages, we don't know the context, so look up the previous message and compare - var uids = [fromuid, touid].sort(function(a, b) { return a > b ? 1 : -1; }); - var key = 'messages:uid:' + uids[0] + ':to:' + uids[1]; - async.waterfall([ - async.apply(db.sortedSetRank, key, messages[0].messageId), - function(index, next) { - // Continue only if this isn't the first message in sorted set - if (index > 0) { - db.getSortedSetRange(key, index-1, index-1, next); - } else { - messages[0].newSet = true; - return next(undefined, messages); - } - }, - function(mid, next) { - Messaging.getMessageFields(mid, ['fromuid', 'timestamp'], next); - } - ], function(err, fields) { - if (err) { - return next(err); - } + user.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture', 'status'], next); + }, + function (users, next) { + messages.forEach(function(message, index) { + message.fromUser = users[index]; + var self = parseInt(message.fromuid, 10) === parseInt(uid, 10); + message.self = self ? 1 : 0; + message.timestampISO = utils.toISOString(message.timestamp); + message.newSet = false; + if (message.hasOwnProperty('edited')) { + message.editedISO = new Date(parseInt(message.edited, 10)).toISOString(); + } + }); - if ( - (parseInt(messages[0].timestamp, 10) > parseInt(fields.timestamp, 10) + (1000*60*5)) || - (parseInt(messages[0].fromuid, 10) !== parseInt(fields.fromuid, 10)) - ) { - // If it's been 5 minutes, this is a new set of messages + async.map(messages, function(message, next) { + Messaging.parse(message.content, message.fromuid, uid, roomId, isNew, function(result) { + message.content = result; + message.cleanedContent = S(result).stripTags().decodeHTMLEntities().s; + next(null, message); + }); + }, next); + }, + function(messages, next) { + if (messages.length > 1) { + // Add a spacer in between messages with time gaps between them + messages = messages.map(function(message, index) { + // Compare timestamps with the previous message, and check if a spacer needs to be added + if (index > 0 && parseInt(message.timestamp, 10) > parseInt(messages[index-1].timestamp, 10) + (1000*60*5)) { + // If it's been 5 minutes, this is a new set of messages + message.newSet = true; + } else if (index > 0 && message.fromuid !== messages[index-1].fromuid) { + // If the previous message was from the other person, this is also a new set + message.newSet = true; + } + + return message; + }); + + next(undefined, messages); + } else { + // For single messages, we don't know the context, so look up the previous message and compare + var key = 'uid:' + uid + ':chat:room:' + roomId + ':mids'; + async.waterfall([ + async.apply(db.sortedSetRank, key, messages[0].messageId), + function(index, next) { + // Continue only if this isn't the first message in sorted set + if (index > 0) { + db.getSortedSetRange(key, index-1, index-1, next); + } else { messages[0].newSet = true; + return next(undefined, messages); } + }, + function(mid, next) { + Messaging.getMessageFields(mid, ['fromuid', 'timestamp'], next); + } + ], function(err, fields) { + if (err) { + return next(err); + } - next(undefined, messages); - }); - } + if ( + (parseInt(messages[0].timestamp, 10) > parseInt(fields.timestamp, 10) + (1000*60*5)) || + (parseInt(messages[0].fromuid, 10) !== parseInt(fields.fromuid, 10)) + ) { + // If it's been 5 minutes, this is a new set of messages + messages[0].newSet = true; + } + + next(undefined, messages); + }); } - ], callback); - }); + } + ], callback); + }; - Messaging.parse = function (message, fromuid, myuid, toUserData, myUserData, isNew, callback) { + Messaging.parse = function (message, fromuid, uid, roomId, isNew, callback) { plugins.fireHook('filter:parse.raw', message, function(err, parsed) { if (err) { return callback(message); @@ -199,9 +202,8 @@ var db = require('./database'), message: message, parsed: parsed, fromuid: fromuid, - myuid: myuid, - toUserData: toUserData, - myUserData: myUserData, + uid: uid, + roomId: roomId, isNew: isNew, parsedMessage: parsed }; @@ -212,15 +214,14 @@ var db = require('./database'), }); }; - Messaging.isNewSet = function(fromuid, touid, mid, callback) { - var uids = sortUids(fromuid, touid), - setKey = 'messages:uid:' + uids[0] + ':to:' + uids[1]; + Messaging.isNewSet = function(uid, roomId, mid, callback) { + var setKey = 'uid:' + uid + ':chat:room:' + roomId + ':mids'; async.waterfall([ async.apply(db.sortedSetRank, setKey, mid), function(index, next) { if (index > 0) { - db.getSortedSetRange(setKey, index-1, index, next); + db.getSortedSetRange(setKey, index - 1, index, next); } else { next(null, true); } @@ -244,23 +245,33 @@ var db = require('./database'), Messaging.getRecentChats = function(uid, start, stop, callback) { - db.getSortedSetRevRange('uid:' + uid + ':chats', start, stop, function(err, uids) { + db.getSortedSetRevRange('uid:' + uid + ':chat:rooms', start, stop, function(err, roomIds) { if (err) { return callback(err); } async.parallel({ unread: function(next) { - db.isSortedSetMembers('uid:' + uid + ':chats:unread', uids, next); + db.isSortedSetMembers('uid:' + uid + ':chat:rooms:unread', roomIds, next); }, users: function(next) { - user.getUsersFields(uids, ['uid', 'username', 'picture', 'status', 'lastonline'] , next); + async.map(roomIds, function(roomId, next) { + db.getSortedSetRevRange('chat:room:' + roomId + ':uids', 0, 3, function(err, uids) { + if (err) { + return next(err); + } + uids = uids.filter(function(value, index, array) { + return value && parseInt(value, 10) !== parseInt(uid, 10); + }); + user.getUsersFields(uids, ['uid', 'username', 'picture', 'status', 'lastonline'] , next); + }); + }, next); }, teasers: function(next) { - async.map(uids, function(fromuid, next) { + async.map(roomIds, function(roomId, next) { Messaging.getMessages({ - fromuid: fromuid, - touid: uid, + uid: uid, + roomId: roomId, isNew: false, count: 1, markRead: false @@ -275,20 +286,25 @@ var db = require('./database'), if (err) { return callback(err); } - - results.users.forEach(function(userData, index) { - if (userData && parseInt(userData.uid, 10)) { - userData.unread = results.unread[index]; - userData.status = user.getStatus(userData); - userData.teaser = results.teasers[index]; - } - }); - - results.users = results.users.filter(function(user) { - return user && parseInt(user.uid, 10); + var rooms = results.users.map(function(users, index) { + var data = { + users: users, + unread: results.unread[index], + roomId: roomIds[index], + teaser: results.teasers[index] + }; + data.users.forEach(function(userData) { + if (userData && parseInt(userData.uid, 10)) { + userData.status = user.getStatus(userData); + } + }); + data.users = data.users.filter(function(user) { + return user && parseInt(user.uid, 10); + }); + return data; }); - callback(null, {users: results.users, nextStart: stop + 1}); + callback(null, {rooms: rooms, nextStart: stop + 1}); }); }); }; diff --git a/src/messaging/create.js b/src/messaging/create.js index b14b8d9868..9389ee6188 100644 --- a/src/messaging/create.js +++ b/src/messaging/create.js @@ -95,7 +95,7 @@ module.exports = function(Messaging) { ], next); }, function (results, next) { - getMessages([mid], fromuid, touid, true, next); + Messaging.getMessagesData([mid], fromuid, touid, true, next); }, function (messages, next) { Messaging.isNewSet(fromuid, touid, mid, next); @@ -119,7 +119,7 @@ module.exports = function(Messaging) { var keys = uids.map(function(uid) { return 'uid:' + uid + ':chat:rooms'; }); - db.sortedSetsAdd(keys, timestamp, roomId, next); + db.sortedSetsAdd(keys, timestamp, roomId, callback); }; Messaging.addMessageToUsers = function(roomId, uids, mid, timestamp, callback) { diff --git a/src/messaging/rooms.js b/src/messaging/rooms.js index e94a696b5c..9f13994b0e 100644 --- a/src/messaging/rooms.js +++ b/src/messaging/rooms.js @@ -43,4 +43,5 @@ module.exports = function(Messaging) { Messaging.getUidsInRoom = function(roomId, start, stop, callback) { db.getSortedSetRange('chat:room:' + roomId + ':uids', start, stop, callback); }; + }; \ No newline at end of file diff --git a/src/routes/debug.js b/src/routes/debug.js index b81938ccc9..0e4691b4d6 100644 --- a/src/routes/debug.js +++ b/src/routes/debug.js @@ -57,7 +57,12 @@ module.exports = function(app, middleware, controllers) { }); router.get('/test', function(req, res) { - res.redirect(404); + //res.redirect(404); + var messaging = require('../messaging'); + + messaging.getRecentChats(1, 0, 9, function(err, data) { + res.json(data); + }) }); app.use(nconf.get('relative_path') + '/debug', router); diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index 701975dcce..599d57ec15 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -22,14 +22,13 @@ SocketModules.chats.get = function(socket, data, callback) { } Messaging.getMessages({ - fromuid: socket.uid, - touid: data.touid, + uid: socket.uid, + roomId: data.roomId, since: data.since, isNew: false }, callback); - // Mark chat as read - Messaging.markRead(socket.uid, data.touid); + Messaging.markRead(socket.uid, data.roomId); }; SocketModules.chats.getRaw = function(socket, data, callback) { From 5d1169e686cf5314b2fb4f2d64fcfb62aeb0de98 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 15 Dec 2015 19:05:32 +0200 Subject: [PATCH 04/27] changed the chat route to /chats/:roomid? --- public/src/client/chats.js | 44 ++++--------------- src/controllers/accounts/chats.js | 73 ++++++++++++------------------- src/messaging.js | 29 ++++-------- src/messaging/rooms.js | 16 +++++++ src/routes/accounts.js | 2 +- 5 files changed, 63 insertions(+), 101 deletions(-) diff --git a/public/src/client/chats.js b/public/src/client/chats.js index 90b5e912ed..9bfc6aeabe 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -1,6 +1,6 @@ 'use strict'; -/* globals define, config, app, ajaxify, utils, socket, templates */ +/* globals define, config, app, ajaxify, utils, socket, templates, Mousetrap, bootbox */ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', 'translator'], function(components, S, sounds, infinitescroll, translator) { var Chats = { @@ -43,8 +43,8 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', }; Chats.addEventListeners = function() { - components.get('chat/recent').on('click', 'li', function(e) { - Chats.switchChat(parseInt($(this).attr('data-uid'), 10), $(this).attr('data-username')); + $('[component="chat/recent"]').on('click', '[component="chat/recent/room"]', function() { + Chats.switchChat($(this).attr('data-roomid')); }); Chats.addSendHandlers(Chats.getRecipientUid(), $('.chat-input'), $('.expanded-chat button[data-action="send"]')); @@ -96,7 +96,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', prev = activeContact.prev(); if (prev.length) { - Chats.switchChat(parseInt(prev.attr('data-uid'), 10), prev.attr('data-username')); + Chats.switchChat(prev.attr('data-roomid')); } }); Mousetrap.bind('ctrl+down', function() { @@ -104,7 +104,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', next = activeContact.next(); if (next.length) { - Chats.switchChat(parseInt(next.attr('data-uid'), 10), next.attr('data-username')); + Chats.switchChat(next.attr('data-roomid')); } }); Mousetrap.bind('up', function(e) { @@ -181,40 +181,15 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', $(this).attr('data-typing', val); }); - sendEl.off('click').on('click', function(e) { + sendEl.off('click').on('click', function() { Chats.sendMessage(toUid, inputEl); inputEl.focus(); return false; }); }; - Chats.switchChat = function(uid, username) { - if (!$('#content [component="chat/messages"]').length) { - return ajaxify.go('chats/' + utils.slugify(username)); - } - - var contactEl = $('.chats-list [data-uid="' + uid + '"]'); - - Chats.loadChatSince(uid, $('.chat-content'), 'recent'); - Chats.addSendHandlers(uid, components.get('chat/input'), $('[data-action="send"]')); - - contactEl - .removeClass('unread') - .addClass('bg-primary') - .siblings().removeClass('bg-primary'); - - components.get('chat/title').text(username); - components.get('chat/messages').attr('data-uid', uid).attr('data-username', username); - components.get('breadcrumb/current').text(username); - components.get('chat/input').focus(); - - if (window.history && window.history.pushState) { - var url = 'chats/' + utils.slugify(username); - - window.history.pushState({ - url: url - }, url, RELATIVE_PATH + '/' + url); - } + Chats.switchChat = function(roomid) { + ajaxify.go('chats/' + roomid); }; Chats.loadChatSince = function(toUid, chatContentEl, since) { @@ -320,13 +295,12 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', }); socket.on('event:chats.edit', function(data) { - var message; data.messages.forEach(function(message) { templates.parse('partials/chat_message', { messages: message }, function(html) { - body = components.get('chat/message', message.messageId); + var body = components.get('chat/message', message.messageId); if (body.length) { body.replaceWith(html); components.get('chat/message', message.messageId).find('.timeago').timeago(); diff --git a/src/controllers/accounts/chats.js b/src/controllers/accounts/chats.js index 1f95db984b..f0e3bcf8f7 100644 --- a/src/controllers/accounts/chats.js +++ b/src/controllers/accounts/chats.js @@ -1,13 +1,11 @@ 'use strict'; -var async = require('async'), - nconf = require('nconf'), +var async = require('async'); + +var messaging = require('../../messaging'); +var meta = require('../../meta'); +var helpers = require('../helpers'); - user = require('../../user'), - messaging = require('../../messaging'), - meta = require('../../meta'), - helpers = require('../helpers'), - utils = require('../../../public/src/utils'); var chatsController = {}; @@ -16,35 +14,15 @@ chatsController.get = function(req, res, callback) { return callback(); } - // In case a userNAME is passed in instead of a slug, the route should not 404 - var slugified = utils.slugify(req.params.userslug); - if (req.params.userslug && req.params.userslug !== slugified) { - return helpers.redirect(res, '/chats/' + slugified); - } - - async.parallel({ - contacts: async.apply(user.getFollowing, req.user.uid, 0, 199), - recentChats: async.apply(messaging.getRecentChats, req.user.uid, 0, 19) - }, function(err, results) { + messaging.getRecentChats(req.user.uid, 0, 19, function(err, recentChats) { if (err) { return callback(err); } - if (results.recentChats.users && results.recentChats.users.length) { - var contactUids = results.recentChats.users.map(function(chatObj) { - return parseInt(chatObj.uid, 10); - }); - - results.contacts = results.contacts.filter(function(contact) { - return contactUids.indexOf(parseInt(contact.uid, 10)) === -1; - }); - } - - if (!req.params.userslug) { + if (!req.params.roomid) { return res.render('chats', { - chats: results.recentChats.users, - nextStart: results.recentChats.nextStart, - contacts: results.contacts, + rooms: recentChats.rooms, + nextStart: recentChats.nextStart, allowed: true, title: '[[pages:chats]]', breadcrumbs: helpers.buildBreadcrumbs([{text: '[[pages:chats]]'}]) @@ -52,21 +30,23 @@ chatsController.get = function(req, res, callback) { } async.waterfall([ - async.apply(user.getUidByUserslug, req.params.userslug), - function(toUid, next) { - if (!toUid || parseInt(toUid, 10) === parseInt(req.user.uid, 10)) { + function (next) { + messaging.isUserInRoom(req.uid, req.params.roomid, next); + }, + function (inRoom, next) { + if (!inRoom) { return callback(); } async.parallel({ - toUser: async.apply(user.getUserFields, toUid, ['uid', 'username']), + users: async.apply(messaging.getUsersInRoom, req.params.roomid, 0, -1), messages: async.apply(messaging.getMessages, { - fromuid: req.user.uid, - touid: toUid, + uid: req.user.uid, + roomId: req.params.roomid, since: 'recent', isNew: false }), - allowed: async.apply(messaging.canMessage, req.user.uid, toUid) + allowed: async.apply(messaging.canMessage, req.user.uid, req.params.roomid) }, next); } ], function(err, data) { @@ -74,15 +54,20 @@ chatsController.get = function(req, res, callback) { return callback(err); } + var usernames = data.users.map(function(user) { + return user && user.username; + }).join(', '); + res.render('chats', { - chats: results.recentChats.users, - nextStart: results.recentChats.nextStart, - contacts: results.contacts, - meta: data.toUser, + roomId: req.params.roomid, + rooms: recentChats.rooms, + nextStart: recentChats.nextStart, + users: data.users, + usernames: usernames, messages: data.messages, allowed: data.allowed, - title: '[[pages:chat, ' + data.toUser.username + ']]', - breadcrumbs: helpers.buildBreadcrumbs([{text: '[[pages:chats]]', url: '/chats'}, {text: data.toUser.username}]) + title: '[[pages:chat, ' + usernames + ']]', + breadcrumbs: helpers.buildBreadcrumbs([{text: '[[pages:chats]]', url: '/chats'}, {text: usernames}]) }); }); }); diff --git a/src/messaging.js b/src/messaging.js index 63b64beca2..83d7f99b87 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -260,7 +260,7 @@ var async = require('async'), if (err) { return next(err); } - uids = uids.filter(function(value, index, array) { + uids = uids.filter(function(value) { return value && parseInt(value, 10) !== parseInt(uid, 10); }); user.getUsersFields(uids, ['uid', 'username', 'picture', 'status', 'lastonline'] , next); @@ -348,20 +348,20 @@ var async = require('async'), }, 1000*60); // wait 60s before sending }; - Messaging.canMessage = function(fromUid, toUid, callback) { - if (parseInt(meta.config.disableChat) === 1 || !fromUid || toUid === fromUid) { + Messaging.canMessage = function(uid, roomId, callback) { + if (parseInt(meta.config.disableChat) === 1 || !uid) { return callback(null, false); } async.waterfall([ function (next) { - user.exists(toUid, next); + Messaging.roomExists(roomId, next); }, - function (exists, next) { - if (!exists) { + function (roomExists, next) { + if (!roomExists) { return callback(null, false); } - user.getUserFields(fromUid, ['banned', 'email:confirmed'], next); + user.getUserFields(uid, ['banned', 'email:confirmed'], next); }, function (userData, next) { if (parseInt(userData.banned, 10) === 1) { @@ -372,20 +372,7 @@ var async = require('async'), return callback(null, false); } - user.getSettings(toUid, next); - }, - function(settings, next) { - if (!settings.restrictChat) { - return callback(null, true); - } - - user.isAdministrator(fromUid, next); - }, - function(isAdmin, next) { - if (isAdmin) { - return callback(null, true); - } - user.isFollowing(toUid, fromUid, next); + next(null, true); } ], callback); }; diff --git a/src/messaging/rooms.js b/src/messaging/rooms.js index 9f13994b0e..73a9885562 100644 --- a/src/messaging/rooms.js +++ b/src/messaging/rooms.js @@ -3,9 +3,14 @@ var async = require('async'); var db = require('../database'); +var user = require('../user'); module.exports = function(Messaging) { + Messaging.isUserInRoom = function(uid, roomId, callback) { + db.isSortedSetMember('chat:room:' + roomId + ':uids', uid, callback); + }; + Messaging.roomExists = function(roomId, callback) { db.exists('chat:room:' + roomId + ':uids', callback); }; @@ -44,4 +49,15 @@ module.exports = function(Messaging) { db.getSortedSetRange('chat:room:' + roomId + ':uids', start, stop, callback); }; + Messaging.getUsersInRoom = function(roomId, start, stop, callback) { + async.waterfall([ + function (next) { + Messaging.getUidsInRoom(roomId, start, stop, next); + }, + function (uids, next) { + user.getUsersFields(uids, ['username', 'uid', 'picture', 'status'], next); + } + ], callback); + }; + }; \ No newline at end of file diff --git a/src/routes/accounts.js b/src/routes/accounts.js index 2d492c6c78..e3a753c2fc 100644 --- a/src/routes/accounts.js +++ b/src/routes/accounts.js @@ -23,5 +23,5 @@ module.exports = function (app, middleware, controllers) { setupPageRoute(app, '/user/:userslug/settings', middleware, accountMiddlewares, controllers.accounts.settings.get); setupPageRoute(app, '/notifications', middleware, [middleware.authenticate], controllers.accounts.notifications.get); - setupPageRoute(app, '/chats/:userslug?', middleware, [middleware.authenticate], controllers.accounts.chats.get); + setupPageRoute(app, '/chats/:roomid?', middleware, [middleware.authenticate], controllers.accounts.chats.get); }; From 6b12bd23288d6ff6a14016c7ae250bcfc10f0d4f Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 15 Dec 2015 19:16:24 +0200 Subject: [PATCH 05/27] update messaging.editMessage --- src/messaging/edit.js | 49 ++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/messaging/edit.js b/src/messaging/edit.js index 7da95f7320..5b2472c4e4 100644 --- a/src/messaging/edit.js +++ b/src/messaging/edit.js @@ -10,35 +10,36 @@ var sockets = require('../socket.io'); module.exports = function(Messaging) { - Messaging.editMessage = function(mid, content, callback) { - async.series([ + Messaging.editMessage = function(uid, mid, roomId, content, callback) { + var uids; + async.waterfall([ function(next) { - // Verify that the message actually changed - Messaging.getMessageField(mid, 'content', function(err, raw) { - if (raw === content) { - // No dice. - return callback(); - } + Messaging.getMessageField(mid, 'content', next); + }, + function (raw, next) { + if (raw === content) { + return callback(); + } - next(); - }); + Messaging.setMessageFields(mid, { + content: content, + edited: Date.now() + }, next); }, - async.apply(Messaging.setMessageFields, mid, { - content: content, - edited: Date.now() - }), - function(next) { - Messaging.getMessageFields(mid, ['fromuid', 'touid'], function(err, data) { - Messaging.getMessagesData([mid], data.fromuid, data.touid, true, function(err, messages) { - sockets.in('uid_' + data.fromuid).emit('event:chats.edit', { - messages: messages - }); - sockets.in('uid_' + data.touid).emit('event:chats.edit', { - messages: messages - }); - next(); + function (next) { + Messaging.getUidsInRoom(roomId, 0, -1, next); + }, + function (_uids, next) { + uids = _uids; + Messaging.getMessagesData([mid], uid, roomId, true, next); + }, + function (messages, next) { + uids.forEach(function(uid) { + sockets.in('uid_' + uid).emit('event:chats.edit', { + messages: messages }); }); + next(); } ], callback); }; From a1ab50bd2d728d83d43f2a4aeb9bffedfb7ed635 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 15 Dec 2015 19:34:48 +0200 Subject: [PATCH 06/27] check admin and priv groups --- src/socket.io/groups.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/socket.io/groups.js b/src/socket.io/groups.js index 3b12693be0..b7e8a9c51e 100644 --- a/src/socket.io/groups.js +++ b/src/socket.io/groups.js @@ -22,6 +22,10 @@ SocketGroups.join = function(socket, data, callback) { return callback(new Error('[[error:invalid-uid]]')); } + if (data.groupName === 'administrators' || groups.isPrivilegeGroup(data.groupName)) { + return callback(new Error('[[error:not-allowed]]')); + } + groups.exists(data.groupName, function(err, exists) { if (err || !exists) { return callback(err || new Error('[[error:no-group]]')); From 43c697a5dfa99ee789a39fa5793a6a4459aeba44 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 15 Dec 2015 19:39:45 +0200 Subject: [PATCH 07/27] edit message --- src/socket.io/modules.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index 599d57ec15..5978060550 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -73,16 +73,16 @@ SocketModules.chats.send = function(socket, data, callback) { }; SocketModules.chats.edit = function(socket, data, callback) { - if (!data) { + if (!data || !data.roomId) { return callback(new Error('[[error:invalid-data]]')); } Messaging.canEdit(data.mid, socket.uid, function(err, allowed) { - if (allowed) { - Messaging.editMessage(data.mid, data.message, callback); - } else { - return callback(new Error('[[error:cant-edit-chat-message]]')); + if (err || !allowed) { + return callback(err || new Error('[[error:cant-edit-chat-message]]')); } + + Messaging.editMessage(socket.uid, data.mid, data.roomId, data.message, callback); }); }; From c5d2f61b01fb2f1802d5ae7b97f7692c006c8f6c Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 15 Dec 2015 20:00:51 +0200 Subject: [PATCH 08/27] more fixes --- src/messaging.js | 8 ++++---- src/messaging/create.js | 41 ++++++++++++++++++++-------------------- src/socket.io/modules.js | 11 +++++------ 3 files changed, 29 insertions(+), 31 deletions(-) diff --git a/src/messaging.js b/src/messaging.js index 83d7f99b87..acf32a256b 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -311,7 +311,7 @@ var async = require('async'), - Messaging.notifyUser = function(fromuid, touid, messageObj) { + Messaging.notifyUser = function(uid, roomId, messageObj) { // Immediate notifications // Recipient Messaging.pushUnreadCount(touid); @@ -355,10 +355,10 @@ var async = require('async'), async.waterfall([ function (next) { - Messaging.roomExists(roomId, next); + Messaging.isUserInRoom(uid, roomId, next); }, - function (roomExists, next) { - if (!roomExists) { + function (inRoom, next) { + if (!inRoom) { return callback(null, false); } user.getUserFields(uid, ['banned', 'email:confirmed'], next); diff --git a/src/messaging/create.js b/src/messaging/create.js index 9389ee6188..4a941e161b 100644 --- a/src/messaging/create.js +++ b/src/messaging/create.js @@ -10,7 +10,7 @@ var db = require('../database'); module.exports = function(Messaging) { - Messaging.newMessage = function(fromuid, toUids, content, timestamp, callback) { + Messaging.newMessage = function(uid, toUids, content, timestamp, callback) { var roomId; async.waterfall([ function (next) { @@ -21,30 +21,30 @@ module.exports = function(Messaging) { }, function (_roomId, next) { roomId = _roomId; - db.sortedSetAdd('chat:room:' + roomId + ':uids', timestamp, fromuid, next); + db.sortedSetAdd('chat:room:' + roomId + ':uids', timestamp, uid, next); }, function (next) { - Messaging.addUsersToRoom(fromuid, toUids, roomId, next); + Messaging.addUsersToRoom(uid, toUids, roomId, next); }, function (next) { - Messaging.sendMessage(fromuid, roomId, content, timestamp, next); + Messaging.sendMessage(uid, roomId, content, timestamp, next); } ], callback); }; - Messaging.sendMessage = function(fromuid, roomId, content, timestamp, callback) { + Messaging.sendMessage = function(uid, roomId, content, timestamp, callback) { async.waterfall([ function (next) { Messaging.checkContent(content, next); }, function (next) { - Messaging.roomExists(roomId, next); + Messaging.isUserInRoom(uid, roomId, next); }, - function (exists, next) { - if (!exists) { - return next(new Error('[[error:chat-room-does-not-exist]]')); + function (inRoom, next) { + if (!inRoom) { + return next(new Error('[[error:not-allowed]]')); } - Messaging.addMessage(fromuid, roomId, content, timestamp, next); + Messaging.addMessage(uid, roomId, content, timestamp, next); } ], callback); }; @@ -74,8 +74,7 @@ module.exports = function(Messaging) { message = { content: content, timestamp: timestamp, - fromuid: fromuid, - roomId: roomId + fromuid: fromuid }; plugins.fireHook('filter:messaging.save', message, next); @@ -95,19 +94,19 @@ module.exports = function(Messaging) { ], next); }, function (results, next) { - Messaging.getMessagesData([mid], fromuid, touid, true, next); + async.parallel({ + messages: async.apply(Messaging.getMessagesData, [mid], fromuid, roomId, true), + isNewSet: async.apply(Messaging.isNewSet, fromuid, roomId, mid) + }, next); }, - function (messages, next) { - Messaging.isNewSet(fromuid, touid, mid, next); - }, - function (isNewSet, next) { - if (!messages || !messages[0]) { + function (results, next) { + if (!results.messages || !results.messages[0]) { return next(null, null); } - messages[0].newSet = isNewSet; - messages[0].mid = mid; - next(null, messages[0]); + results.messages[0].newSet = results.isNewSet; + results.messages[0].mid = mid; + next(null, results.messages[0]); } ], callback); }; diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index 5978060550..0d1d82b34c 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -40,12 +40,11 @@ SocketModules.chats.getRaw = function(socket, data, callback) { }; SocketModules.chats.send = function(socket, data, callback) { - if (!data) { + if (!data || !data.roomId) { return callback(new Error('[[error:invalid-data]]')); } - var now = Date.now(), - touid = parseInt(data.touid, 10); + var now = Date.now(); // Websocket rate limiting socket.lastChatMessageTime = socket.lastChatMessageTime || 0; @@ -55,17 +54,17 @@ SocketModules.chats.send = function(socket, data, callback) { socket.lastChatMessageTime = now; } - Messaging.canMessage(socket.uid, touid, function(err, allowed) { + Messaging.canMessage(socket.uid, data.roomId, function(err, allowed) { if (err || !allowed) { return callback(err || new Error('[[error:chat-restricted]]')); } - Messaging.addMessage(socket.uid, touid, data.message, now, function(err, message) { + Messaging.sendMessage(socket.uid, data.roomId, data.message, now, function(err, message) { if (err) { return callback(err); } - Messaging.notifyUser(socket.uid, touid, message); + Messaging.notifyUser(socket.uid, data.roomId, message); callback(); }); From d5bccfb292a67d41d1b9e01550085b16e1907484 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 16 Dec 2015 09:30:55 +0200 Subject: [PATCH 09/27] delete chats on user delete --- src/user/delete.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/user/delete.js b/src/user/delete.js index 2fc444da42..5b822fcd7f 100644 --- a/src/user/delete.js +++ b/src/user/delete.js @@ -55,6 +55,9 @@ module.exports = function(User) { function (next) { deleteVotes(uid, next); }, + function (next) { + deleteChats(uid, next); + }, function (next) { async.parallel([ function(next) { @@ -97,6 +100,7 @@ module.exports = function(User) { 'uid:' + uid + ':favourites', 'uid:' + uid + ':followed_tids', 'user:' + uid + ':settings', 'uid:' + uid + ':topics', 'uid:' + uid + ':posts', 'uid:' + uid + ':chats', 'uid:' + uid + ':chats:unread', + 'uid:' + uid + ':chat:rooms', 'uid:' + uid + ':chat:rooms:unread', 'uid:' + uid + ':upvote', 'uid:' + uid + ':downvote', 'uid:' + uid + ':ignored:cids', 'uid:' + uid + ':flag:pids' ]; @@ -145,6 +149,29 @@ module.exports = function(User) { }); } + function deleteChats(uid, callback) { + async.waterfall([ + function (next) { + db.getSortedSetRange('uid:' + uid + ':chat:rooms', 0, -1, next); + }, + function (roomIds, next) { + var userKeys = roomIds.map(function(roomId) { + return 'uid:' + uid + ':chat:room:' + roomId + ':mids'; + }); + var roomKeys = roomIds.map(function(roomId) { + return 'chat:room:' + roomId + ':uids'; + }); + + async.parallel([ + async.apply(db.sortedSetsRemove, roomKeys, uid), + async.apply(db.deleteAll, userKeys) + ], next); + } + ], function(err) { + callback(err); + }); + } + function deleteUserIps(uid, callback) { async.waterfall([ function (next) { From 29d8b9c18120bda48c8a22ccb04a2ee52547c4c9 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 16 Dec 2015 10:09:00 +0200 Subject: [PATCH 10/27] more room changes app.openChat --- public/src/app.js | 13 ++---- public/src/client/chats.js | 89 +++++++++++++++++++------------------- public/src/modules/chat.js | 40 ++++++++--------- src/socket.io/modules.js | 10 ++--- 4 files changed, 73 insertions(+), 79 deletions(-) diff --git a/public/src/app.js b/public/src/app.js index a66f0c4151..10e192ba63 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -239,11 +239,7 @@ app.cacheBuster = null; } }; - app.openChat = function (username, touid) { - if (username === app.user.username) { - return app.alertError('[[error:cant-chat-with-yourself]]'); - } - + app.openChat = function (roomId) { if (!app.user.uid) { return app.alertError('[[error:not-logged-in]]'); } @@ -255,13 +251,12 @@ app.cacheBuster = null; chat.focusInput(chatModal); } - if (!chat.modalExists(touid)) { + if (!chat.modalExists(roomId)) { chat.createModal({ - username: username, - touid: touid + roomId: roomId }, loadAndCenter); } else { - loadAndCenter(chat.getModal(touid)); + loadAndCenter(chat.getModal(roomId)); } }); }; diff --git a/public/src/client/chats.js b/public/src/client/chats.js index 9bfc6aeabe..2eb7c17380 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -28,8 +28,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', Chats.initialised = true; - if (ajaxify.data.hasOwnProperty('meta') && ajaxify.data.meta.hasOwnProperty('uid')) { - // This is an active chat, focus on the input box + if (ajaxify.data.hasOwnProperty('roomId')) { components.get('chat/input').focus(); } }; @@ -47,20 +46,20 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', Chats.switchChat($(this).attr('data-roomid')); }); - Chats.addSendHandlers(Chats.getRecipientUid(), $('.chat-input'), $('.expanded-chat button[data-action="send"]')); + Chats.addSendHandlers(ajaxify.data.roomId, $('.chat-input'), $('.expanded-chat button[data-action="send"]')); $('[data-action="pop-out"]').on('click', function() { - var username = $('.expanded-chat').attr('data-username'), - uid = Chats.getRecipientUid(), - text = components.get('chat/input').val(); + + var text = components.get('chat/input').val(); + var roomId = ajaxify.data.roomId; if (app.previousUrl && app.previousUrl.match(/chats/)) { ajaxify.go('chats', function() { - app.openChat(username, uid); + app.openChat(roomId); }, true); } else { window.history.go(-1); - app.openChat(username, uid); + app.openChat(roomId); } $(window).one('action:chat.loaded', function() { @@ -87,7 +86,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', } }); - Chats.addSinceHandler(Chats.getRecipientUid(), $('.expanded-chat .chat-content'), $('.expanded-chat [data-since]')); + Chats.addSinceHandler(ajaxify.data.roomId, $('.expanded-chat .chat-content'), $('.expanded-chat [data-since]')); }; Chats.addHotkeys = function() { @@ -121,6 +120,9 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', Chats.prepEdit = function(inputEl, messageId) { socket.emit('modules.chats.getRaw', { mid: messageId }, function(err, raw) { + if (err) { + return app.alertError(err.message); + } // Populate the input field with the raw message content if (inputEl.val().length === 0) { // By setting the `data-mid` attribute, I tell the chat code that I am editing a @@ -134,39 +136,40 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', Chats.delete = function(messageId) { translator.translate('[[modules:chat.delete_message_confirm]]', function(translated) { bootbox.confirm(translated, function(ok) { - if (ok) { - socket.emit('modules.chats.delete', { - messageId: messageId - }, function(err) { - if (err) { - return app.alertError(err.message); - } + if (!ok) { + return; + } - // Remove message from list - components.get('chat/message', messageId).slideUp('slow', function() { - $(this).remove(); - }); + socket.emit('modules.chats.delete', { + messageId: messageId + }, function(err) { + if (err) { + return app.alertError(err.message); + } + + components.get('chat/message', messageId).slideUp('slow', function() { + $(this).remove(); }); - } + }); }); }); }; - Chats.addSinceHandler = function(toUid, chatContentEl, sinceEl) { + Chats.addSinceHandler = function(roomId, chatContentEl, sinceEl) { sinceEl.on('click', function() { var since = $(this).attr('data-since'); sinceEl.removeClass('selected'); $(this).addClass('selected'); - Chats.loadChatSince(toUid, chatContentEl, since); + Chats.loadChatSince(roomId, chatContentEl, since); return false; }); }; - Chats.addSendHandlers = function(toUid, inputEl, sendEl) { + Chats.addSendHandlers = function(roomId, inputEl, sendEl) { inputEl.off('keypress').on('keypress', function(e) { if (e.which === 13 && !e.shiftKey) { - Chats.sendMessage(toUid, inputEl); + Chats.sendMessage(roomId, inputEl); return false; } }); @@ -177,12 +180,12 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', return; } - Chats.notifyTyping(toUid, val); + Chats.notifyTyping(roomId, val); $(this).attr('data-typing', val); }); sendEl.off('click').on('click', function() { - Chats.sendMessage(toUid, inputEl); + Chats.sendMessage(roomId, inputEl); inputEl.focus(); return false; }); @@ -192,11 +195,11 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', ajaxify.go('chats/' + roomid); }; - Chats.loadChatSince = function(toUid, chatContentEl, since) { - if (!toUid) { + Chats.loadChatSince = function(roomId, chatContentEl, since) { + if (!roomId) { return; } - socket.emit('modules.chats.get', {touid: toUid, since: since}, function(err, messages) { + socket.emit('modules.chats.get', {roomId: roomId, since: since}, function(err, messages) { if (err) { return app.alertError(err.message); } @@ -210,12 +213,9 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', Chats.addGlobalEventListeners = function() { $(window).on('resize', Chats.resizeMainWindow); $(window).on('mousemove keypress click', function() { - if (newMessage) { - var recipientUid = Chats.getRecipientUid(); - if (recipientUid) { - socket.emit('modules.chats.markRead', recipientUid); - newMessage = false; - } + if (newMessage && ajaxify.data.roomId) { + socket.emit('modules.chats.markRead', ajaxify.data.roomId); + newMessage = false; } }); }; @@ -325,14 +325,14 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', Chats.setActive(); }; - Chats.notifyTyping = function(toUid, typing) { + Chats.notifyTyping = function(roomId, typing) { socket.emit('modules.chats.user' + (typing ? 'Start' : 'Stop') + 'Typing', { - touid: toUid, + roomId: roomId, fromUid: app.user.uid }); }; - Chats.sendMessage = function(toUid, inputEl) { + Chats.sendMessage = function(roomId, inputEl) { var msg = inputEl.val(), mid = inputEl.attr('data-mid'); @@ -349,7 +349,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', if (!mid) { socket.emit('modules.chats.send', { - touid: toUid, + roomId: roomId, message: msg }, function(err) { if (err) { @@ -360,7 +360,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', } sounds.play('chat-outgoing'); - Chats.notifyTyping(toUid, false); + Chats.notifyTyping(roomId, false); }); } else { socket.emit('modules.chats.edit', { @@ -371,7 +371,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', return app.alertError(err.message); } - Chats.notifyTyping(toUid, false); + Chats.notifyTyping(roomId, false); }); } }; @@ -385,9 +385,8 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', }; Chats.setActive = function() { - var recipientUid = Chats.getRecipientUid(); - if (recipientUid) { - socket.emit('modules.chats.markRead', recipientUid); + if (ajaxify.data.roomId) { + socket.emit('modules.chats.markRead', ajaxify.data.roomId); $('.expanded-chat input').focus(); } $('.chats-list li').removeClass('bg-primary'); diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index b294107955..8ccc475741 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -129,9 +129,9 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra dropdownEl.click(function() { if (!ajaxify.currentPage.match(/^chats\//)) { - app.openChat(userObj.username, userObj.uid); + app.openChat(roomObj.roomId); } else { - ajaxify.go('chats/' + utils.slugify(userObj.username)); + ajaxify.go('chats/' + roomObj.roomId); } }); }); @@ -156,12 +156,12 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra chatModal.css('zIndex', topZ + 1); }; - module.getModal = function(touid) { - return $('#chat-modal-' + touid); + module.getModal = function(roomId) { + return $('#chat-modal-' + roomId); }; - module.modalExists = function(touid) { - return $('#chat-modal-' + touid).length !== 0; + module.modalExists = function(roomId) { + return $('#chat-modal-' + roomId).length !== 0; }; function checkStatus(chatModal) { @@ -182,8 +182,8 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra uuid = utils.generateUUID(), dragged = false; - chatModal.attr('id', 'chat-modal-' + data.touid); - chatModal.attr('touid', data.touid); + chatModal.attr('id', 'chat-modal-' + data.roomId); + chatModal.attr('roomId', data.roomId); chatModal.attr('intervalId', 0); chatModal.attr('UUID', uuid); chatModal.css('position', 'fixed'); @@ -254,7 +254,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra chatModal.on('mousemove keypress click', function() { if (newMessage) { - socket.emit('modules.chats.markRead', data.touid); + socket.emit('modules.chats.markRead', data.roomId); newMessage = false; } }); @@ -270,15 +270,15 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra Chats.delete(messageId); }); - Chats.addSinceHandler(chatModal.attr('touid'), chatModal.find('.chat-content'), chatModal.find('[data-since]')); + Chats.addSinceHandler(chatModal.attr('roomId'), chatModal.find('.chat-content'), chatModal.find('[data-since]')); - Chats.addSendHandlers(chatModal.attr('touid'), chatModal.find('#chat-message-input'), chatModal.find('#chat-message-send-btn')); + Chats.addSendHandlers(chatModal.attr('roomId'), chatModal.find('#chat-message-input'), chatModal.find('#chat-message-send-btn')); - Chats.loadChatSince(chatModal.attr('touid'), chatModal.find('.chat-content'), 'recent'); + Chats.loadChatSince(chatModal.attr('roomId'), chatModal.find('.chat-content'), 'recent'); checkStatus(chatModal); - module.canMessage(data.touid, function(err) { + module.canMessage(data.roomId, function(err) { if (err) { // Disable the text input chatModal.find('input[type="text"]').attr('disabled', true); @@ -287,7 +287,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra taskbar.push('chat', chatModal.attr('UUID'), { title: data.username, - touid: data.touid, + roomId: data.roomId, icon: 'fa-comment', state: '' }); @@ -311,7 +311,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra chatModal.remove(); chatModal.data('modal', null); taskbar.discard('chat', chatModal.attr('UUID')); - Chats.notifyTyping(chatModal.attr('touid'), false); + Chats.notifyTyping(chatModal.attr('roomId'), false); if (chatModal.attr('data-mobile')) { module.disableMobileBehaviour(chatModal); @@ -334,14 +334,14 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra }; module.load = function(uuid) { - var chatModal = $('div[UUID="'+uuid+'"]'); + var chatModal = $('div[UUID="' + uuid + '"]'); chatModal.removeClass('hide'); checkStatus(chatModal); taskbar.updateActive(uuid); Chats.scrollToBottom(chatModal.find('.chat-content')); module.bringModalToTop(chatModal); module.focusInput(chatModal); - socket.emit('modules.chats.markRead', chatModal.attr('touid')); + socket.emit('modules.chats.markRead', chatModal.attr('roomId')); var env = utils.findBootstrapEnvironment(); if (env === 'xs' || env === 'sm') { @@ -380,13 +380,13 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra taskbar.minimize('chat', uuid); clearInterval(chatModal.attr('intervalId')); chatModal.attr('intervalId', 0); - Chats.notifyTyping(chatModal.attr('touid'), false); + Chats.notifyTyping(chatModal.attr('roomId'), false); }; module.toggleNew = taskbar.toggleNew; - module.canMessage = function(toUid, callback) { - socket.emit('modules.chats.canMessage', toUid, callback); + module.canMessage = function(roomId, callback) { + socket.emit('modules.chats.canMessage', roomId, callback); }; diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index 0d1d82b34c..e1c1d78b8f 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -17,7 +17,7 @@ var meta = require('../meta'), /* Chat */ SocketModules.chats.get = function(socket, data, callback) { - if(!data) { + if(!data || !data.roomId) { return callback(new Error('[[error:invalid-data]]')); } @@ -97,14 +97,14 @@ SocketModules.chats.delete = function(socket, data, callback) { }); } -SocketModules.chats.canMessage = function(socket, toUid, callback) { - Messaging.canMessage(socket.uid, toUid, function(err, allowed) { +SocketModules.chats.canMessage = function(socket, roomId, callback) { + Messaging.canMessage(socket.uid, roomId, function(err, allowed) { callback(!allowed ? new Error('[[error:chat-restricted]]') : undefined); }); }; -SocketModules.chats.markRead = function(socket, touid, callback) { - Messaging.markRead(socket.uid, touid, function(err) { +SocketModules.chats.markRead = function(socket, roomId, callback) { + Messaging.markRead(socket.uid, roomId, function(err) { if (err) { return callback(err); } From 2ac34ba39114229132227b54e63da714197dd1b0 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 16 Dec 2015 10:16:44 +0200 Subject: [PATCH 11/27] fix recepientUid --- public/src/client/chats.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/client/chats.js b/public/src/client/chats.js index 2eb7c17380..4c1986be9f 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -390,7 +390,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', $('.expanded-chat input').focus(); } $('.chats-list li').removeClass('bg-primary'); - $('.chats-list li[data-uid="' + recipientUid + '"]').addClass('bg-primary'); + $('.chats-list li[data-roomid="' + ajaxify.data.roomId + '"]').addClass('bg-primary'); }; Chats.parseMessage = function(data, callback) { From f8a6666e1a54a925dc0f80ecfb66aae8e7fd157e Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 16 Dec 2015 11:15:43 +0200 Subject: [PATCH 12/27] chat notify changes --- src/messaging.js | 112 ++++++++++++--------------------- src/messaging/create.js | 1 + src/messaging/notifications.js | 104 ++++++++++++++++++++++++++++++ src/socket.io/modules.js | 36 ++++++++++- src/user.js | 28 ++++++--- 5 files changed, 199 insertions(+), 82 deletions(-) create mode 100644 src/messaging/notifications.js diff --git a/src/messaging.js b/src/messaging.js index acf32a256b..5cfbca1318 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -4,17 +4,15 @@ var async = require('async'), winston = require('winston'), S = require('string'), - nconf = require('nconf'), + db = require('./database'), user = require('./user'), plugins = require('./plugins'), meta = require('./meta'), - emailer = require('./emailer'), utils = require('../public/src/utils'), notifications = require('./notifications'), - userNotifications = require('./user/notifications'), - sockets = require('./socket.io'); + userNotifications = require('./user/notifications'); (function(Messaging) { @@ -23,6 +21,7 @@ var async = require('async'), require('./messaging/edit')(Messaging); require('./messaging/rooms')(Messaging); require('./messaging/unread')(Messaging); + require('./messaging/notifications')(Messaging); Messaging.notifyQueue = {}; // Only used to notify a user of a new chat message, see Messaging.notifyUser @@ -309,46 +308,50 @@ var async = require('async'), }); }; + Messaging.canMessageUser = function(uid, toUid, callback) { + if (parseInt(meta.config.disableChat) === 1 || !uid || uid === toUid) { + return callback(null, false); + } + async.waterfall([ + function (next) { + user.exists(toUid, next); + }, + function (exists, next) { + if (!exists) { + return callback(null, false); + } + user.getUserFields(uid, ['banned', 'email:confirmed'], next); + }, + function (userData, next) { + if (parseInt(userData.banned, 10) === 1) { + return callback(null, false); + } - Messaging.notifyUser = function(uid, roomId, messageObj) { - // Immediate notifications - // Recipient - Messaging.pushUnreadCount(touid); - sockets.in('uid_' + touid).emit('event:chats.receive', { - withUid: fromuid, - message: messageObj, - self: 0 - }); - // Sender - Messaging.pushUnreadCount(fromuid); - sockets.in('uid_' + fromuid).emit('event:chats.receive', { - withUid: touid, - message: messageObj, - self: 1 - }); + if (parseInt(meta.config.requireEmailConfirmation, 10) === 1 && parseInt(userData['email:confirmed'], 10) !== 1) { + return callback(null, false); + } - // Delayed notifications - var queueObj = Messaging.notifyQueue[fromuid + ':' + touid]; - if (queueObj) { - queueObj.message.content += '\n' + messageObj.content; - clearTimeout(queueObj.timeout); - } else { - queueObj = Messaging.notifyQueue[fromuid + ':' + touid] = { - message: messageObj - }; - } + user.getSettings(toUid, next); + }, + function(settings, next) { + if (!settings.restrictChat) { + return callback(null, true); + } - queueObj.timeout = setTimeout(function() { - sendNotifications(fromuid, touid, queueObj.message, function(err) { - if (!err) { - delete Messaging.notifyQueue[fromuid + ':' + touid]; + user.isAdministrator(uid, next); + }, + function(isAdmin, next) { + if (isAdmin) { + return callback(null, true); } - }); - }, 1000*60); // wait 60s before sending + user.isFollowing(toUid, uid, next); + } + ], callback); + }; - Messaging.canMessage = function(uid, roomId, callback) { + Messaging.canMessageRoom = function(uid, roomId, callback) { if (parseInt(meta.config.disableChat) === 1 || !uid) { return callback(null, false); } @@ -378,39 +381,4 @@ var async = require('async'), }; - function sendNotifications(fromuid, touid, messageObj, callback) { - user.isOnline(touid, function(err, isOnline) { - if (err || isOnline) { - return callback(err); - } - - notifications.create({ - bodyShort: '[[notifications:new_message_from, ' + messageObj.fromUser.username + ']]', - bodyLong: messageObj.content, - nid: 'chat_' + fromuid + '_' + touid, - from: fromuid, - path: '/chats/' + messageObj.fromUser.username - }, function(err, notification) { - if (!err && notification) { - notifications.push(notification, [touid], callback); - } - }); - - user.getSettings(messageObj.toUser.uid, function(err, settings) { - if (settings.sendChatNotifications && !parseInt(meta.config.disableEmailSubscriptions, 10)) { - emailer.send('notif_chat', touid, { - subject: '[[email:notif.chat.subject, ' + messageObj.fromUser.username + ']]', - username: messageObj.toUser.username, - userslug: utils.slugify(messageObj.toUser.username), - summary: '[[notifications:new_message_from, ' + messageObj.fromUser.username + ']]', - message: messageObj, - site_title: meta.config.title || 'NodeBB', - url: nconf.get('url'), - fromUserslug: utils.slugify(messageObj.fromUser.username) - }); - } - }); - }); - } - }(exports)); diff --git a/src/messaging/create.js b/src/messaging/create.js index 4a941e161b..360f800e81 100644 --- a/src/messaging/create.js +++ b/src/messaging/create.js @@ -106,6 +106,7 @@ module.exports = function(Messaging) { results.messages[0].newSet = results.isNewSet; results.messages[0].mid = mid; + results.messages[0].roomId = roomId; next(null, results.messages[0]); } ], callback); diff --git a/src/messaging/notifications.js b/src/messaging/notifications.js new file mode 100644 index 0000000000..b1b8564659 --- /dev/null +++ b/src/messaging/notifications.js @@ -0,0 +1,104 @@ +'use strict'; + +var async = require('async'); +var nconf = require('nconf'); + +var user = require('../user'); +var emailer = require('../emailer'); +var notifications = require('./notifications'); +var meta = require('../meta'); +var utils = require('../../public/src/utils'); +var sockets = require('../socket.io'); + +module.exports = function(Messaging) { + + Messaging.notifyUsersInRoom = function(fromUid, roomId, messageObj) { + Messaging.getUidsInRoom(roomId, 0, -1, function(err, uids) { + if (err) { + return; + } + + var data = { + roomId: roomId, + fromUid: fromUid, + message: messageObj + }; + uids.forEach(function(uid) { + data.self = parseInt(uid, 10) === parseInt(fromUid) ? 1 : 0; + Messaging.pushUnreadCount(uid); + sockets.in('uid_' + uid).emit('event:chats.receive', data); + }); + + // Delayed notifications + var queueObj = Messaging.notifyQueue[fromUid + ':' + roomId]; + if (queueObj) { + queueObj.message.content += '\n' + messageObj.content; + clearTimeout(queueObj.timeout); + } else { + queueObj = Messaging.notifyQueue[fromUid + ':' + roomId] = { + message: messageObj + }; + } + + queueObj.timeout = setTimeout(function() { + sendNotifications(fromUid, uids, roomId, queueObj.message, function(err) { + if (!err) { + delete Messaging.notifyQueue[fromUid + ':' + roomId]; + } + }); + }, 1000*60); // wait 60s before sending + }); + }; + + function sendNotifications(fromuid, uids, roomId, messageObj, callback) { + user.isOnline(uids, function(err, isOnline) { + if (err) { + return callback(err); + } + + uids = uids.filter(function(uid, index) { + return isOnline[index]; + }); + + if (!uids.length) { + return callback(); + } + + notifications.create({ + bodyShort: '[[notifications:new_message_from, ' + messageObj.fromUser.username + ']]', + bodyLong: messageObj.content, + nid: 'chat_' + fromuid + '_' + roomId, + from: fromuid, + path: '/chats/' + messageObj.fromUser.username + }, function(err, notification) { + if (!err && notification) { + notifications.push(notification, uids, callback); + } + }); + + if (parseInt(meta.config.disableEmailSubscriptions, 10) === 1) { + return callback(); + } + + user.getMultipleUserSettings(uids, function(err, userSettings) { + if (err) { + return callback(err); + } + userSettings = userSettings.filter(function(settings) { + return settings && settings.sendChatNotifications; + }); + async.each(userSettings, function(settings, next) { + emailer.send('notif_chat', settings.uid, { + subject: '[[email:notif.chat.subject, ' + messageObj.fromUser.username + ']]', + summary: '[[notifications:new_message_from, ' + messageObj.fromUser.username + ']]', + message: messageObj, + site_title: meta.config.title || 'NodeBB', + url: nconf.get('url'), + fromUserslug: utils.slugify(messageObj.fromUser.username) + }, next); + }, callback); + + }); + }); + } +}; \ No newline at end of file diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index e1c1d78b8f..bb0de7b268 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -39,6 +39,36 @@ SocketModules.chats.getRaw = function(socket, data, callback) { Messaging.getMessageField(data.mid, 'content', callback); }; +SocketModules.chats.newMessage = function(socket, data, callback) { + if (!data) { + return callback(new Error('[[error:invalid-data]]')); + } + var now = Date.now(); + // Websocket rate limiting + socket.lastChatMessageTime = socket.lastChatMessageTime || 0; + if (now - socket.lastChatMessageTime < 200) { + return callback(new Error('[[error:too-many-messages]]')); + } else { + socket.lastChatMessageTime = now; + } + + Messaging.canMessageUser(socket.uid, data.touid, function(err, allowed) { + if (err || !allowed) { + return callback(err || new Error('[[error:chat-restricted]]')); + } + + Messaging.newMessage(socket.uid, [data.touid], data.content, now, function(err, message) { + if (err) { + return callback(err); + } + + Messaging.notifyUsersInRoom(socket.uid, message.roomId, message); + + callback(); + }); + }); +}; + SocketModules.chats.send = function(socket, data, callback) { if (!data || !data.roomId) { return callback(new Error('[[error:invalid-data]]')); @@ -54,7 +84,7 @@ SocketModules.chats.send = function(socket, data, callback) { socket.lastChatMessageTime = now; } - Messaging.canMessage(socket.uid, data.roomId, function(err, allowed) { + Messaging.canMessageRoom(socket.uid, data.roomId, function(err, allowed) { if (err || !allowed) { return callback(err || new Error('[[error:chat-restricted]]')); } @@ -64,7 +94,7 @@ SocketModules.chats.send = function(socket, data, callback) { return callback(err); } - Messaging.notifyUser(socket.uid, data.roomId, message); + Messaging.notifyUsersInRoom(socket.uid, data.roomId, message); callback(); }); @@ -95,7 +125,7 @@ SocketModules.chats.delete = function(socket, data, callback) { Messaging.deleteMessage(data.messageId, data.roomId, callback); } }); -} +}; SocketModules.chats.canMessage = function(socket, roomId, callback) { Messaging.canMessage(socket.uid, roomId, function(err, allowed) { diff --git a/src/user.js b/src/user.js index 4b64b52dbd..7a9213a963 100644 --- a/src/user.js +++ b/src/user.js @@ -132,13 +132,27 @@ var async = require('async'), }; User.isOnline = function(uid, callback) { - db.sortedSetScore('users:online', uid, function(err, lastonline) { - if (err) { - return callback(err); - } - var isOnline = Date.now() - parseInt(lastonline, 10) < 300000; - callback(null, isOnline); - }); + if (Array.isArray(uid)) { + db.sortedSetScores('users:online', uid, function(err, lastonline) { + if (err) { + return callback(err); + } + var now = Date.now(); + var isOnline = uid.map(function(uid, index) { + return now - lastonline[index] < 300000; + }); + callback(null, isOnline); + }); + } else { + db.sortedSetScore('users:online', uid, function(err, lastonline) { + if (err) { + return callback(err); + } + var isOnline = Date.now() - parseInt(lastonline, 10) < 300000; + callback(null, isOnline); + }); + } + }; User.exists = function(uid, callback) { From 8fab270852dfa7a57e1328bed6555b8a2ffc59a3 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 16 Dec 2015 11:30:10 +0200 Subject: [PATCH 13/27] leaveRoom method, small fixes --- public/src/app.js | 7 +++++++ src/controllers/accounts/chats.js | 2 +- src/messaging.js | 2 -- src/messaging/notifications.js | 2 ++ src/messaging/rooms.js | 11 +++++++++++ src/socket.io/modules.js | 2 +- 6 files changed, 22 insertions(+), 4 deletions(-) diff --git a/public/src/app.js b/public/src/app.js index 10e192ba63..2f6e759609 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -261,6 +261,13 @@ app.cacheBuster = null; }); }; + app.newChat = function (touid) { + if (!app.user.uid) { + return app.alertError('[[error:not-logged-in]]'); + } + + }; + var titleObj = { active: false, interval: undefined, diff --git a/src/controllers/accounts/chats.js b/src/controllers/accounts/chats.js index f0e3bcf8f7..30c73fcce1 100644 --- a/src/controllers/accounts/chats.js +++ b/src/controllers/accounts/chats.js @@ -46,7 +46,7 @@ chatsController.get = function(req, res, callback) { since: 'recent', isNew: false }), - allowed: async.apply(messaging.canMessage, req.user.uid, req.params.roomid) + allowed: async.apply(messaging.canMessageRoom, req.user.uid, req.params.roomid) }, next); } ], function(err, data) { diff --git a/src/messaging.js b/src/messaging.js index 5cfbca1318..a71000854f 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -23,8 +23,6 @@ var async = require('async'), require('./messaging/unread')(Messaging); require('./messaging/notifications')(Messaging); - Messaging.notifyQueue = {}; // Only used to notify a user of a new chat message, see Messaging.notifyUser - var terms = { day: 86400000, week: 604800000, diff --git a/src/messaging/notifications.js b/src/messaging/notifications.js index b1b8564659..046c07c88e 100644 --- a/src/messaging/notifications.js +++ b/src/messaging/notifications.js @@ -12,6 +12,8 @@ var sockets = require('../socket.io'); module.exports = function(Messaging) { + Messaging.notifyQueue = {}; // Only used to notify a user of a new chat message, see Messaging.notifyUser + Messaging.notifyUsersInRoom = function(fromUid, roomId, messageObj) { Messaging.getUidsInRoom(roomId, 0, -1, function(err, uids) { if (err) { diff --git a/src/messaging/rooms.js b/src/messaging/rooms.js index 73a9885562..c9612a9e75 100644 --- a/src/messaging/rooms.js +++ b/src/messaging/rooms.js @@ -45,6 +45,17 @@ module.exports = function(Messaging) { ], callback); }; + Messaging.leaveRoom = function(uid, roomId, callback) { + async.waterfall([ + function (next) { + db.sortedSetRemove('chat:room:' + roomId + ':uids', uid, next); + }, + function (next) { + db.sortedSetRemove('uid:' + uid + ':chat:rooms', roomId, next); + } + ], callback); + }; + Messaging.getUidsInRoom = function(roomId, start, stop, callback) { db.getSortedSetRange('chat:room:' + roomId + ':uids', start, stop, callback); }; diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index bb0de7b268..c22f48fef0 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -128,7 +128,7 @@ SocketModules.chats.delete = function(socket, data, callback) { }; SocketModules.chats.canMessage = function(socket, roomId, callback) { - Messaging.canMessage(socket.uid, roomId, function(err, allowed) { + Messaging.canMessageRoom(socket.uid, roomId, function(err, allowed) { callback(!allowed ? new Error('[[error:chat-restricted]]') : undefined); }); }; From 784ff07907670680ba7009234484cd5d64c41637 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 16 Dec 2015 13:35:24 +0200 Subject: [PATCH 14/27] ability to add/remove users from chat rooms, tagsinput --- public/src/app.js | 31 ++++++++--- public/src/client/account/header.js | 2 +- public/src/client/chats.js | 37 +++++++++++++ public/src/client/topic/postTools.js | 4 +- public/src/modules/autocomplete.js | 3 + public/src/modules/chat.js | 6 +- src/controllers/accounts/chats.js | 10 +++- src/messaging.js | 4 +- src/messaging/create.js | 27 +-------- src/messaging/rooms.js | 46 +++++++++++++--- src/socket.io/modules.js | 82 +++++++++++++++++++++------- 11 files changed, 183 insertions(+), 69 deletions(-) diff --git a/public/src/app.js b/public/src/app.js index 2f6e759609..c0c5fb6332 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -1,5 +1,5 @@ "use strict"; -/*global io, templates, ajaxify, utils, bootbox, overrides, socket, config, Visibility*/ +/*global templates, translator, ajaxify, utils, bootbox, overrides, socket, config, Visibility*/ var app = app || {}; @@ -159,7 +159,7 @@ app.cacheBuster = null; } app.currentRoom = ''; }); - } + }; function highlightNavigationLink() { var path = window.location.pathname; @@ -251,12 +251,21 @@ app.cacheBuster = null; chat.focusInput(chatModal); } - if (!chat.modalExists(roomId)) { - chat.createModal({ - roomId: roomId - }, loadAndCenter); - } else { + if (chat.modalExists(roomId)) { loadAndCenter(chat.getModal(roomId)); + } else { + socket.emit('modules.chats.getUsersInRoom', {roomId: roomId}, function(err, users) { + if (err) { + return app.alertError(err.message); + } + users = users.filter(function(user) { + return user && parseInt(user.uid, 10) !== parseInt(app.user.uid, 10); + }); + chat.createModal({ + roomId: roomId, + users: users + }, loadAndCenter); + }); } }); }; @@ -266,6 +275,12 @@ app.cacheBuster = null; return app.alertError('[[error:not-logged-in]]'); } + socket.emit('modules.chats.newRoom', {touid: touid}, function(err, roomId) { + if (err) { + return app.alertError(err.message); + } + app.openChat(roomId); + }); }; var titleObj = { @@ -410,7 +425,7 @@ app.cacheBuster = null; function handleStatusChange() { $('[component="header/usercontrol"] [data-status]').off('click').on('click', function(e) { var status = $(this).attr('data-status'); - socket.emit('user.setStatus', status, function(err, data) { + socket.emit('user.setStatus', status, function(err) { if(err) { return app.alertError(err.message); } diff --git a/public/src/client/account/header.js b/public/src/client/account/header.js index b67f9dd4a9..9c04d4a84d 100644 --- a/public/src/client/account/header.js +++ b/public/src/client/account/header.js @@ -30,7 +30,7 @@ define('forum/account/header', [ }); components.get('account/chat').on('click', function() { - app.openChat($('.account-username').html(), theirid); + app.newChat(theirid); }); components.get('account/ban').on('click', banAccount); diff --git a/public/src/client/chats.js b/public/src/client/chats.js index 4c1986be9f..9064b2ef17 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -18,6 +18,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', } Chats.addEventListeners(); + Chats.createTagsInput(ajaxify.data.roomId, ajaxify.data.users); if (env === 'md' || env === 'lg') { Chats.resizeMainWindow(); @@ -191,6 +192,42 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', }); }; + Chats.createTagsInput = function(roomId, users) { + var tagEl = $('.users-tag-input'); + + tagEl.tagsinput({ + confirmKeys: [13, 44], + trimValue: true + }); + + if (users && users.length) { + users.forEach(function(user) { + tagEl.tagsinput('add', user.username); + }); + } + + tagEl.on('itemAdded', function(event) { + if (event.item === app.user.username) { + return; + } + socket.emit('modules.chats.addUserToRoom', {roomId: roomId, username: event.item}, function(err) { + if (err && err.message === '[[error:no-user]]') { + tagEl.tagsinput('remove', event.item); + } + }); + }); + + tagEl.on('itemRemoved', function(event) { + socket.emit('modules.chats.removeUserFromRoom', {roomId: roomId, username: event.item}); + }); + + var input = $('.users-tag-container').find('.bootstrap-tagsinput input'); + + require(['autocomplete'], function(autocomplete) { + autocomplete.user(input); + }); + }; + Chats.switchChat = function(roomid) { ajaxify.go('chats/' + roomid); }; diff --git a/public/src/client/topic/postTools.js b/public/src/client/topic/postTools.js index c971a7e5fc..0ae05ec7a5 100644 --- a/public/src/client/topic/postTools.js +++ b/public/src/client/topic/postTools.js @@ -405,12 +405,10 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator }); } - - function openChat(button) { var post = button.parents('[data-pid]'); - app.openChat(post.attr('data-username'), post.attr('data-uid')); + app.newChat(post.attr('data-uid')); button.parents('.btn-group').find('.dropdown-toggle').click(); return false; } diff --git a/public/src/modules/autocomplete.js b/public/src/modules/autocomplete.js index a66fff7264..ecf15c2ead 100644 --- a/public/src/modules/autocomplete.js +++ b/public/src/modules/autocomplete.js @@ -10,6 +10,9 @@ define('autocomplete', function() { app.loadJQueryUI(function() { input.autocomplete({ delay: 100, + open: function() { + $(this).autocomplete('widget').css('z-index', 20000); + }, select: onselect, source: function(request, response) { socket.emit('user.search', {query: request.term}, function(err, result) { diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index 8ccc475741..a419583ced 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -229,7 +229,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra components.get('chat/input').val(text); }); - ajaxify.go('chats/' + utils.slugify(data.username)); + ajaxify.go('chats/' + chatModal.attr('roomId')); module.close(chatModal); } @@ -240,7 +240,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra module.bringModalToTop(chatModal); if (!dragged) { - chatModal.find('#chat-message-input').focus(); + //chatModal.find('#chat-message-input').focus(); } else { dragged = false; } @@ -274,6 +274,8 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra Chats.addSendHandlers(chatModal.attr('roomId'), chatModal.find('#chat-message-input'), chatModal.find('#chat-message-send-btn')); + Chats.createTagsInput(data.roomId, data.users); + Chats.loadChatSince(chatModal.attr('roomId'), chatModal.find('.chat-content'), 'recent'); checkStatus(chatModal); diff --git a/src/controllers/accounts/chats.js b/src/controllers/accounts/chats.js index 30c73fcce1..63e324d1f5 100644 --- a/src/controllers/accounts/chats.js +++ b/src/controllers/accounts/chats.js @@ -14,7 +14,7 @@ chatsController.get = function(req, res, callback) { return callback(); } - messaging.getRecentChats(req.user.uid, 0, 19, function(err, recentChats) { + messaging.getRecentChats(req.uid, 0, 19, function(err, recentChats) { if (err) { return callback(err); } @@ -41,12 +41,12 @@ chatsController.get = function(req, res, callback) { async.parallel({ users: async.apply(messaging.getUsersInRoom, req.params.roomid, 0, -1), messages: async.apply(messaging.getMessages, { - uid: req.user.uid, + uid: req.uid, roomId: req.params.roomid, since: 'recent', isNew: false }), - allowed: async.apply(messaging.canMessageRoom, req.user.uid, req.params.roomid) + allowed: async.apply(messaging.canMessageRoom, req.uid, req.params.roomid) }, next); } ], function(err, data) { @@ -54,6 +54,10 @@ chatsController.get = function(req, res, callback) { return callback(err); } + data.users = data.users.filter(function(user) { + return user && parseInt(user.uid, 10) !== req.uid; + }); + var usernames = data.users.map(function(user) { return user && user.username; }).join(', '); diff --git a/src/messaging.js b/src/messaging.js index a71000854f..70e535a0d9 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -274,7 +274,9 @@ var async = require('async'), markRead: false }, function(err, teaser) { teaser = teaser[0]; - teaser.content = S(teaser.content).stripTags().decodeHTMLEntities().s; + if (teaser && teaser.content) { + teaser.content = S(teaser.content).stripTags().decodeHTMLEntities().s; + } next(err, teaser); }); }, next); diff --git a/src/messaging/create.js b/src/messaging/create.js index 360f800e81..da9be535a8 100644 --- a/src/messaging/create.js +++ b/src/messaging/create.js @@ -9,29 +9,6 @@ var db = require('../database'); module.exports = function(Messaging) { - - Messaging.newMessage = function(uid, toUids, content, timestamp, callback) { - var roomId; - async.waterfall([ - function (next) { - Messaging.checkContent(content, next); - }, - function (next) { - db.incrObjectField('global', 'nextChatRoomId', next); - }, - function (_roomId, next) { - roomId = _roomId; - db.sortedSetAdd('chat:room:' + roomId + ':uids', timestamp, uid, next); - }, - function (next) { - Messaging.addUsersToRoom(uid, toUids, roomId, next); - }, - function (next) { - Messaging.sendMessage(uid, roomId, content, timestamp, next); - } - ], callback); - }; - Messaging.sendMessage = function(uid, roomId, content, timestamp, callback) { async.waterfall([ function (next) { @@ -87,7 +64,7 @@ module.exports = function(Messaging) { }, function (uids, next) { async.parallel([ - async.apply(Messaging.updateChatTime, roomId, uids, timestamp), + async.apply(Messaging.addRoomToUsers, roomId, uids, timestamp), async.apply(Messaging.addMessageToUsers, roomId, uids, mid, timestamp), async.apply(Messaging.markRead, fromuid, roomId), async.apply(Messaging.markUnread, uids, roomId) @@ -112,7 +89,7 @@ module.exports = function(Messaging) { ], callback); }; - Messaging.updateChatTime = function(roomId, uids, timestamp, callback) { + Messaging.addRoomToUsers = function(roomId, uids, timestamp, callback) { if (!uids.length) { return callback(); } diff --git a/src/messaging/rooms.js b/src/messaging/rooms.js index c9612a9e75..434a789508 100644 --- a/src/messaging/rooms.js +++ b/src/messaging/rooms.js @@ -7,6 +7,29 @@ var user = require('../user'); module.exports = function(Messaging) { + Messaging.newRoom = function(uid, toUids, callback) { + var roomId; + var now = Date.now(); + async.waterfall([ + function (next) { + db.incrObjectField('global', 'nextChatRoomId', next); + }, + function (_roomId, next) { + roomId = _roomId; + db.sortedSetAdd('chat:room:' + roomId + ':uids', now, uid, next); + }, + function (next) { + Messaging.addUsersToRoom(uid, toUids, roomId, next); + }, + function (next) { + Messaging.addRoomToUsers(roomId, [uid].concat(toUids), now, next); + }, + function (next) { + next(null, roomId); + } + ], callback); + }; + Messaging.isUserInRoom = function(uid, roomId, callback) { db.isSortedSetMember('chat:room:' + roomId + ':uids', uid, callback); }; @@ -27,31 +50,40 @@ module.exports = function(Messaging) { }); }; - Messaging.addUsersToRoom = function(fromuid, toUids, roomId, callback) { + Messaging.addUsersToRoom = function(uid, uids, roomId, callback) { async.waterfall([ function (next) { - Messaging.isRoomOwner(fromuid, roomId, next); + Messaging.isRoomOwner(uid, roomId, next); }, function (isOwner, next) { if (!isOwner) { return next(new Error('[[error:cant-add-users-to-chat-room]]')); } var now = Date.now(); - var timestamps = toUids.map(function() { + var timestamps = uids.map(function() { return now; }); - db.sortedSetAdd('chat:room:' + roomId + ':uids', timestamps, toUids, next); + db.sortedSetAdd('chat:room:' + roomId + ':uids', timestamps, uids, next); } ], callback); }; - Messaging.leaveRoom = function(uid, roomId, callback) { + Messaging.removeUsersFromRoom = function(uid, uids, roomId, callback) { async.waterfall([ function (next) { - db.sortedSetRemove('chat:room:' + roomId + ':uids', uid, next); + Messaging.isRoomOwner(uid, roomId, next); + }, + function (isOwner, next) { + if (!isOwner) { + return next(new Error('[[error:cant-add-users-to-chat-room]]')); + } + db.sortedSetRemove('chat:room:' + roomId + ':uids', uids, next); }, function (next) { - db.sortedSetRemove('uid:' + uid + ':chat:rooms', roomId, next); + var keys = uids.map(function(uid) { + return 'uid:' + uid + ':chat:rooms'; + }); + db.sortedSetsRemove(keys, roomId, next); } ], callback); }; diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index c22f48fef0..575012955e 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -1,14 +1,13 @@ "use strict"; -var meta = require('../meta'), - Messaging = require('../messaging'), - utils = require('../../public/src/utils'), - - async = require('async'), - - server = require('./'), - - SocketModules = { +var async = require('async'); +var meta = require('../meta'); +var Messaging = require('../messaging'); +var utils = require('../../public/src/utils'); +var server = require('./'); +var user = require('../user'); + +var SocketModules = { chats: {}, sounds: {}, settings: {} @@ -39,7 +38,7 @@ SocketModules.chats.getRaw = function(socket, data, callback) { Messaging.getMessageField(data.mid, 'content', callback); }; -SocketModules.chats.newMessage = function(socket, data, callback) { +SocketModules.chats.newRoom = function(socket, data, callback) { if (!data) { return callback(new Error('[[error:invalid-data]]')); } @@ -57,15 +56,7 @@ SocketModules.chats.newMessage = function(socket, data, callback) { return callback(err || new Error('[[error:chat-restricted]]')); } - Messaging.newMessage(socket.uid, [data.touid], data.content, now, function(err, message) { - if (err) { - return callback(err); - } - - Messaging.notifyUsersInRoom(socket.uid, message.roomId, message); - - callback(); - }); + Messaging.newRoom(socket.uid, [data.touid], callback); }); }; @@ -101,6 +92,59 @@ SocketModules.chats.send = function(socket, data, callback) { }); }; +SocketModules.chats.getUsersInRoom = function(socket, data, callback) { + if (!data || !data.roomId) { + return callback(new Error('[[error:invalid-data]]')); + } + + async.waterfall([ + function (next) { + Messaging.isUserInRoom(socket.uid, data.roomId, next); + }, + function (inRoom, next) { + if (!inRoom) { + return next(new Error('[[error:not-allowerd]]')); + } + Messaging.getUsersInRoom(data.roomId, 0, -1, next); + } + ], callback); +}; + +SocketModules.chats.addUserToRoom = function(socket, data, callback) { + if (!data || !data.roomId || !data.username) { + return callback(new Error('[[error:invalid-data]]')); + } + async.waterfall([ + function (next) { + user.getUidByUsername(data.username, next); + }, + function (uid, next) { + if (!uid) { + return next(new Error('[[error:no-user]]')); + } + Messaging.addUsersToRoom(socket.uid, [uid], data.roomId, next); + } + ], callback); +}; + +SocketModules.chats.removeUserFromRoom = function(socket, data, callback) { + if (!data || !data.roomId) { + return callback(new Error('[[error:invalid-data]]')); + } + async.waterfall([ + function (next) { + user.getUidByUsername(data.username, next); + }, + function (uid, next) { + if (!uid) { + return next(new Error('[[error:no-user]]')); + } + + Messaging.removeUsersFromRoom(socket.uid, [uid], data.roomId, next); + } + ], callback); +}; + SocketModules.chats.edit = function(socket, data, callback) { if (!data || !data.roomId) { return callback(new Error('[[error:invalid-data]]')); From d8810ceae90c050eb9770a3e0d5eeff90cac3628 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 16 Dec 2015 15:09:14 +0200 Subject: [PATCH 15/27] send messages --- public/src/client/chats.js | 41 +--------------------------------- public/src/modules/chat.js | 13 +++++------ src/messaging/create.js | 3 +++ src/messaging/notifications.js | 5 ++--- src/messaging/unread.js | 3 ++- src/socket.io/modules.js | 14 ++++++++++-- 6 files changed, 25 insertions(+), 54 deletions(-) diff --git a/public/src/client/chats.js b/public/src/client/chats.js index 9064b2ef17..7f0f319f9f 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -34,14 +34,6 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', } }; - Chats.getRecipientUid = function() { - return parseInt($('.expanded-chat').attr('data-uid'), 10); - }; - - Chats.isCurrentChat = function(uid) { - return Chats.getRecipientUid() === parseInt(uid, 10); - }; - Chats.addEventListeners = function() { $('[component="chat/recent"]').on('click', '[component="chat/recent/room"]', function() { Chats.switchChat($(this).attr('data-roomid')); @@ -280,42 +272,11 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', Chats.addSocketListeners = function() { socket.on('event:chats.receive', function(data) { - if (Chats.isCurrentChat(data.withUid)) { + if (parseInt(data.roomId, 10) === parseInt(ajaxify.data.roomId, 10)) { newMessage = data.self === 0; data.message.self = data.self; Chats.appendChatMessage($('.expanded-chat .chat-content'), data.message); - } else { - var contactEl = $('[component="chat/recent"] li[data-uid="' + data.withUid + '"]'), - userKey = parseInt(data.withUid, 10) === parseInt(data.message.fromuid, 10) ? 'fromUser' : 'toUser'; - - // Spawn a new contact if required - templates.parse('partials/chat_contacts', { - contacts: [{ - uid: data.message[userKey].uid, - username: data.message[userKey].username, - status: data.message[userKey].status, - picture: data.message[userKey].picture, - 'icon:text': data.message[userKey]['icon:text'], - 'icon:bgColor': data.message[userKey]['icon:bgColor'], - teaser: { - content: data.message.cleanedContent, - timestampISO: new Date(Date.now()).toISOString() - } - }] - }, function(html) { - translator.translate(html, function(translatedHTML) { - if (contactEl.length) { - contactEl.replaceWith(translatedHTML); - } else { - $('[component="chat/recent"]').prepend(translatedHTML); - } - - // Mark that contact list entry unread - $('.chats-list li[data-uid="' + data.withUid + '"]').addClass('unread').find('.timeago').timeago(); - app.alternatingTitle('[[modules:chat.user_has_messaged_you, ' + data.message.fromUser.username + ']]'); - }); - }); } }); diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index a419583ced..949537aae4 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -20,14 +20,12 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra socket.on('event:chats.receive', function(data) { var username = data.message.fromUser.username; - var isSelf = parseInt(data.message.fromUser.uid, 10) === parseInt(app.user.uid, 10); + var isSelf = data.self === 1; data.message.self = data.self; - if (isSelf) { - username = data.message.toUser.username; - } + newMessage = data.self === 0; - if (module.modalExists(data.withUid)) { - var modal = module.getModal(data.withUid); + if (module.modalExists(data.roomId)) { + var modal = module.getModal(data.roomId); Chats.appendChatMessage(modal.find('.chat-content'), data.message); @@ -49,8 +47,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra } } else { module.createModal({ - username: username, - touid: data.withUid, + roomId: data.roomId, silent: true }, function(modal) { module.toggleNew(modal.attr('UUID'), true, true); diff --git a/src/messaging/create.js b/src/messaging/create.js index da9be535a8..f9ccae958b 100644 --- a/src/messaging/create.js +++ b/src/messaging/create.js @@ -21,6 +21,7 @@ module.exports = function(Messaging) { if (!inRoom) { return next(new Error('[[error:not-allowed]]')); } + Messaging.addMessage(uid, roomId, content, timestamp, next); } ], callback); @@ -34,11 +35,13 @@ module.exports = function(Messaging) { if (content.length > (meta.config.maximumChatMessageLength || 1000)) { return callback(new Error('[[error:chat-message-too-long]]')); } + callback(); }; Messaging.addMessage = function(fromuid, roomId, content, timestamp, callback) { var mid; var message; + async.waterfall([ function (next) { Messaging.checkContent(content, next); diff --git a/src/messaging/notifications.js b/src/messaging/notifications.js index 046c07c88e..c9aa10eb33 100644 --- a/src/messaging/notifications.js +++ b/src/messaging/notifications.js @@ -5,7 +5,7 @@ var nconf = require('nconf'); var user = require('../user'); var emailer = require('../emailer'); -var notifications = require('./notifications'); +var notifications = require('../notifications'); var meta = require('../meta'); var utils = require('../../public/src/utils'); var sockets = require('../socket.io'); @@ -26,7 +26,7 @@ module.exports = function(Messaging) { message: messageObj }; uids.forEach(function(uid) { - data.self = parseInt(uid, 10) === parseInt(fromUid) ? 1 : 0; + data.self = parseInt(uid, 10) === parseInt(fromUid) ? 1 : 0; Messaging.pushUnreadCount(uid); sockets.in('uid_' + uid).emit('event:chats.receive', data); }); @@ -99,7 +99,6 @@ module.exports = function(Messaging) { fromUserslug: utils.slugify(messageObj.fromUser.username) }, next); }, callback); - }); }); } diff --git a/src/messaging/unread.js b/src/messaging/unread.js index 6933d9c1fb..4bceaab3ce 100644 --- a/src/messaging/unread.js +++ b/src/messaging/unread.js @@ -36,7 +36,8 @@ module.exports = function(Messaging) { var keys = uids.map(function(uid) { return 'uid:' + uid + ':chat:rooms:unread'; }); - db.sortedSetAdd(keys, Date.now(), roomId, next); + + db.sortedSetsAdd(keys, Date.now(), roomId, next); } ], callback); }; diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index 575012955e..cfebbfb848 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -197,10 +197,20 @@ SocketModules.chats.userStopTyping = function(socket, data, callback) { }; function sendTypingNotification(event, socket, data, callback) { - if (!socket.uid || !data) { + if (!socket.uid || !data || !data.roomId) { return; } - server.in('uid_' + data.touid).emit(event, data.fromUid); + + Messaging.getUidsInRoom(data.roomId, 0, -1, function(err, uids) { + if (err) { + return callback(err); + } + uids.forEach(function(uid) { + if (socket.uid !== parseInt(uid, 10)) { + server.in('uid_' + uid).emit(event, data.fromUid); + } + }); + }); } SocketModules.chats.getRecentChats = function(socket, data, callback) { From 1bd9b364e4931e66c0269acbe62133232b4d4c3a Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 16 Dec 2015 15:12:06 +0200 Subject: [PATCH 16/27] fix notifs, dont send to self, fix notification path to roomId --- src/messaging/notifications.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/messaging/notifications.js b/src/messaging/notifications.js index c9aa10eb33..9fca3c952b 100644 --- a/src/messaging/notifications.js +++ b/src/messaging/notifications.js @@ -59,7 +59,7 @@ module.exports = function(Messaging) { } uids = uids.filter(function(uid, index) { - return isOnline[index]; + return isOnline[index] && parseInt(fromuid, 10) !== parseInt(uid, 10); }); if (!uids.length) { @@ -71,7 +71,7 @@ module.exports = function(Messaging) { bodyLong: messageObj.content, nid: 'chat_' + fromuid + '_' + roomId, from: fromuid, - path: '/chats/' + messageObj.fromUser.username + path: '/chats/' + messageObj.roomId }, function(err, notification) { if (!err && notification) { notifications.push(notification, uids, callback); From 832a20e59e46140dad058517619e1a0f6a34d399 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 16 Dec 2015 15:43:01 +0200 Subject: [PATCH 17/27] markRead fixes --- public/src/client/chats.js | 4 ++++ src/controllers/accounts/chats.js | 24 +++++++++++------------- src/messaging/create.js | 2 +- src/messaging/rooms.js | 6 +++--- src/user/notifications.js | 4 +--- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/public/src/client/chats.js b/public/src/client/chats.js index 7f0f319f9f..984c3b4f55 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -209,6 +209,10 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', }); }); + tagEl.on('beforeItemRemove', function(event) { + event.cancel = !ajaxify.data.owner; + }); + tagEl.on('itemRemoved', function(event) { socket.emit('modules.chats.removeUserFromRoom', {roomId: roomId, username: event.item}); }); diff --git a/src/controllers/accounts/chats.js b/src/controllers/accounts/chats.js index 63e324d1f5..5399d04d66 100644 --- a/src/controllers/accounts/chats.js +++ b/src/controllers/accounts/chats.js @@ -46,7 +46,8 @@ chatsController.get = function(req, res, callback) { since: 'recent', isNew: false }), - allowed: async.apply(messaging.canMessageRoom, req.uid, req.params.roomid) + allowed: async.apply(messaging.canMessageRoom, req.uid, req.params.roomid), + owner: async.apply(messaging.isRoomOwner, req.uid, req.params.roomid) }, next); } ], function(err, data) { @@ -58,21 +59,18 @@ chatsController.get = function(req, res, callback) { return user && parseInt(user.uid, 10) !== req.uid; }); - var usernames = data.users.map(function(user) { + data.usernames = data.users.map(function(user) { return user && user.username; }).join(', '); - res.render('chats', { - roomId: req.params.roomid, - rooms: recentChats.rooms, - nextStart: recentChats.nextStart, - users: data.users, - usernames: usernames, - messages: data.messages, - allowed: data.allowed, - title: '[[pages:chat, ' + usernames + ']]', - breadcrumbs: helpers.buildBreadcrumbs([{text: '[[pages:chats]]', url: '/chats'}, {text: usernames}]) - }); + + data.roomId = req.params.roomid; + data.rooms = recentChats.rooms; + data.nextStart = recentChats.nextStart; + data.title = '[[pages:chat, ' + data.usernames + ']]'; + data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[pages:chats]]', url: '/chats'}, {text: data.usernames}]); + + res.render('chats', data); }); }); }; diff --git a/src/messaging/create.js b/src/messaging/create.js index f9ccae958b..0309cab4b9 100644 --- a/src/messaging/create.js +++ b/src/messaging/create.js @@ -69,12 +69,12 @@ module.exports = function(Messaging) { async.parallel([ async.apply(Messaging.addRoomToUsers, roomId, uids, timestamp), async.apply(Messaging.addMessageToUsers, roomId, uids, mid, timestamp), - async.apply(Messaging.markRead, fromuid, roomId), async.apply(Messaging.markUnread, uids, roomId) ], next); }, function (results, next) { async.parallel({ + markRead: async.apply(Messaging.markRead, fromuid, roomId), messages: async.apply(Messaging.getMessagesData, [mid], fromuid, roomId, true), isNewSet: async.apply(Messaging.isNewSet, fromuid, roomId, mid) }, next); diff --git a/src/messaging/rooms.js b/src/messaging/rooms.js index 434a789508..092b5c85f2 100644 --- a/src/messaging/rooms.js +++ b/src/messaging/rooms.js @@ -53,10 +53,10 @@ module.exports = function(Messaging) { Messaging.addUsersToRoom = function(uid, uids, roomId, callback) { async.waterfall([ function (next) { - Messaging.isRoomOwner(uid, roomId, next); + Messaging.isUserInRoom(uid, roomId, next); }, - function (isOwner, next) { - if (!isOwner) { + function (inRoom, next) { + if (!inRoom) { return next(new Error('[[error:cant-add-users-to-chat-room]]')); } var now = Date.now(); diff --git a/src/user/notifications.js b/src/user/notifications.js index 477a9cf53f..b670b42169 100644 --- a/src/user/notifications.js +++ b/src/user/notifications.js @@ -129,9 +129,7 @@ var async = require('async'), notification.path = pidToPaths[notification.pid] || notification.path || ''; - if (notification.nid.startsWith('chat')) { - notification.path = '/chats/' + notification.user.userslug; - } else if (notification.nid.startsWith('follow')) { + if (notification.nid.startsWith('follow')) { notification.path = '/user/' + notification.user.userslug; } From da5891bcf6079bc1efe861de6ec0bed2848a4852 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 16 Dec 2015 15:49:39 +0200 Subject: [PATCH 18/27] remove debug --- src/routes/debug.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/routes/debug.js b/src/routes/debug.js index 0e4691b4d6..b81938ccc9 100644 --- a/src/routes/debug.js +++ b/src/routes/debug.js @@ -57,12 +57,7 @@ module.exports = function(app, middleware, controllers) { }); router.get('/test', function(req, res) { - //res.redirect(404); - var messaging = require('../messaging'); - - messaging.getRecentChats(1, 0, 9, function(err, data) { - res.json(data); - }) + res.redirect(404); }); app.use(nconf.get('relative_path') + '/debug', router); From 002e931a9c255ed18dc0b782e4a6b95718922551 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 16 Dec 2015 16:41:39 +0200 Subject: [PATCH 19/27] rev --- src/messaging/rooms.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/messaging/rooms.js b/src/messaging/rooms.js index 092b5c85f2..5cc3459291 100644 --- a/src/messaging/rooms.js +++ b/src/messaging/rooms.js @@ -89,7 +89,7 @@ module.exports = function(Messaging) { }; Messaging.getUidsInRoom = function(roomId, start, stop, callback) { - db.getSortedSetRange('chat:room:' + roomId + ':uids', start, stop, callback); + db.getSortedSetRevRange('chat:room:' + roomId + ':uids', start, stop, callback); }; Messaging.getUsersInRoom = function(roomId, start, stop, callback) { From f4e502c793a86e3b97ee1994432502d86a8d2499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 16 Dec 2015 21:52:42 +0200 Subject: [PATCH 20/27] simpler isNewSet --- src/messaging.js | 26 +++++++------------------- src/messaging/create.js | 10 +++++++--- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/messaging.js b/src/messaging.js index 70e535a0d9..784c66dda0 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -211,32 +211,20 @@ var async = require('async'), }); }; - Messaging.isNewSet = function(uid, roomId, mid, callback) { + Messaging.isNewSet = function(uid, roomId, timestamp, callback) { var setKey = 'uid:' + uid + ':chat:room:' + roomId + ':mids'; async.waterfall([ - async.apply(db.sortedSetRank, setKey, mid), - function(index, next) { - if (index > 0) { - db.getSortedSetRange(setKey, index - 1, index, next); - } else { - next(null, true); - } - }, - function(mids, next) { - if (typeof mids !== 'boolean' && mids && mids.length) { - db.getObjects(['message:' + mids[0], 'message:' + mids[1]], next); - } else { - next(null, mids); - } + function(next) { + db.getSortedSetRevRangeWithScores(setKey, 0, 0, next); }, function(messages, next) { - if (typeof messages !== 'boolean' && messages && messages.length) { - next(null, parseInt(messages[1].timestamp, 10) > parseInt(messages[0].timestamp, 10) + (1000*60*5)); + if (messages && messages.length) { + next(null, parseInt(timestamp, 10) > parseInt(messages[0].score, 10) + (1000 * 60 * 5)); } else { - next(null, messages); + next(null, true); } - } + } ], callback); }; diff --git a/src/messaging/create.js b/src/messaging/create.js index 0309cab4b9..d01eaf6292 100644 --- a/src/messaging/create.js +++ b/src/messaging/create.js @@ -41,6 +41,7 @@ module.exports = function(Messaging) { Messaging.addMessage = function(fromuid, roomId, content, timestamp, callback) { var mid; var message; + var isNewSet; async.waterfall([ function (next) { @@ -63,6 +64,10 @@ module.exports = function(Messaging) { db.setObject('message:' + mid, message, next); }, function (next) { + Messaging.isNewSet(fromuid, roomId, timestamp, next); + }, + function (_isNewSet, next) { + isNewSet = _isNewSet; db.getSortedSetRange('chat:room:' + roomId + ':uids', 0, -1, next); }, function (uids, next) { @@ -75,8 +80,7 @@ module.exports = function(Messaging) { function (results, next) { async.parallel({ markRead: async.apply(Messaging.markRead, fromuid, roomId), - messages: async.apply(Messaging.getMessagesData, [mid], fromuid, roomId, true), - isNewSet: async.apply(Messaging.isNewSet, fromuid, roomId, mid) + messages: async.apply(Messaging.getMessagesData, [mid], fromuid, roomId, true) }, next); }, function (results, next) { @@ -84,7 +88,7 @@ module.exports = function(Messaging) { return next(null, null); } - results.messages[0].newSet = results.isNewSet; + results.messages[0].newSet = isNewSet; results.messages[0].mid = mid; results.messages[0].roomId = roomId; next(null, results.messages[0]); From f71fd0a3ecfc39435cfdf54d98ff82d7316272f8 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 17 Dec 2015 11:22:15 +0200 Subject: [PATCH 21/27] only allow users in the room to get raw message content --- public/src/client/chats.js | 8 ++++---- public/src/modules/chat.js | 2 +- src/socket.io/modules.js | 15 ++++++++++++--- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/public/src/client/chats.js b/public/src/client/chats.js index 984c3b4f55..e853bffec1 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -64,7 +64,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', .on('click', '[data-action="edit"]', function() { var messageId = $(this).parents('[data-mid]').attr('data-mid'); var inputEl = components.get('chat/input'); - Chats.prepEdit(inputEl, messageId); + Chats.prepEdit(inputEl, messageId, ajaxify.data.roomId); }) .on('click', '[data-action="delete"]', function() { var messageId = $(this).parents('[data-mid]').attr('data-mid'); @@ -106,13 +106,13 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', var lastMid = message.attr('data-mid'); var inputEl = components.get('chat/input'); - Chats.prepEdit(inputEl, lastMid); + Chats.prepEdit(inputEl, lastMid, ajaxify.data.roomId); } }); }; - Chats.prepEdit = function(inputEl, messageId) { - socket.emit('modules.chats.getRaw', { mid: messageId }, function(err, raw) { + Chats.prepEdit = function(inputEl, messageId, roomId) { + socket.emit('modules.chats.getRaw', { mid: messageId, roomId: roomId }, function(err, raw) { if (err) { return app.alertError(err.message); } diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index 949537aae4..d3972e3708 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -260,7 +260,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra .on('click', '[data-action="edit"]', function() { var messageId = $(this).parents('[data-mid]').attr('data-mid'); var inputEl = chatModal.find('[component="chat/input"]'); - Chats.prepEdit(inputEl, messageId); + Chats.prepEdit(inputEl, messageId, data.roomId); }) .on('click', '[data-action="delete"]', function() { var messageId = $(this).parents('[data-mid]').attr('data-mid'); diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index cfebbfb848..3f22f19500 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -31,11 +31,20 @@ SocketModules.chats.get = function(socket, data, callback) { }; SocketModules.chats.getRaw = function(socket, data, callback) { - if(!data || !data.hasOwnProperty('mid')) { + if (!data || !data.hasOwnProperty('mid')) { return callback(new Error('[[error:invalid-data]]')); } - - Messaging.getMessageField(data.mid, 'content', callback); + async.waterfall([ + function (next) { + Messaging.isUserInRoom(socket.uid, data.roomId, next); + }, + function (inRoom, next) { + if (!inRoom) { + return next(new Error('[[error:not-allowed]]')); + } + Messaging.getMessageField(data.mid, 'content', next); + } + ], callback); }; SocketModules.chats.newRoom = function(socket, data, callback) { From d2b54ae0f89b71d7cbde8a6c3c03f7b045c7bfb0 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 17 Dec 2015 11:47:32 +0200 Subject: [PATCH 22/27] display less derpy chat list --- public/src/modules/chat.js | 8 ++++---- src/controllers/accounts/chats.js | 2 +- src/messaging.js | 6 +++++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index d3972e3708..f6159e28e1 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -113,16 +113,16 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra '' : '
    ' + userObj['icon:text'] + '
    ') + ' ' + - userObj.username + ''; + roomObj.usernames + ''; } dropdownEl = $('
  • ') .attr('data-roomId', roomObj.roomId) .appendTo(chatsListEl); - roomObj.users.forEach(function(userObj) { - dropdownEl.append(createUserImage(userObj)); - }); + if (roomObj.lastUser) { + dropdownEl.append(createUserImage(roomObj.lastUser)); + } dropdownEl.click(function() { if (!ajaxify.currentPage.match(/^chats\//)) { diff --git a/src/controllers/accounts/chats.js b/src/controllers/accounts/chats.js index 5399d04d66..2c777e7266 100644 --- a/src/controllers/accounts/chats.js +++ b/src/controllers/accounts/chats.js @@ -68,7 +68,7 @@ chatsController.get = function(req, res, callback) { data.rooms = recentChats.rooms; data.nextStart = recentChats.nextStart; data.title = '[[pages:chat, ' + data.usernames + ']]'; - data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[pages:chats]]', url: '/chats'}, {text: data.usernames}]); + data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[pages:chats]]', url: '/chats'}, {text: data.roomId}]); res.render('chats', data); }); diff --git a/src/messaging.js b/src/messaging.js index 784c66dda0..eb254a235b 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -224,7 +224,7 @@ var async = require('async'), } else { next(null, true); } - } + } ], callback); }; @@ -288,6 +288,10 @@ var async = require('async'), data.users = data.users.filter(function(user) { return user && parseInt(user.uid, 10); }); + data.lastUser = data.users[0]; + data.usernames = data.users.map(function(user) { + return user.username; + }).join(', '); return data; }); From 992ca0a3517c10903219128d4479e8659ed3fa30 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 17 Dec 2015 11:56:28 +0200 Subject: [PATCH 23/27] fix taskbar icon --- public/src/modules/chat.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index f6159e28e1..57d5811de2 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -214,8 +214,6 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra }); }); - chatModal.find('#chat-with-name').html(data.username); - chatModal.find('#chat-close-btn').on('click', function() { module.close(chatModal); }); @@ -236,9 +234,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra chatModal.on('click', function() { module.bringModalToTop(chatModal); - if (!dragged) { - //chatModal.find('#chat-message-input').focus(); - } else { + if (dragged) { dragged = false; } }); @@ -285,7 +281,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra }); taskbar.push('chat', chatModal.attr('UUID'), { - title: data.username, + title: data.users.length ? data.users[0].username : '', roomId: data.roomId, icon: 'fa-comment', state: '' From 3d79e0750b417b835681a3331abdb46e8d99932e Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 17 Dec 2015 12:05:35 +0200 Subject: [PATCH 24/27] fix edit delete for rooms --- public/src/client/chats.js | 8 +++++--- public/src/modules/chat.js | 2 +- src/socket.io/modules.js | 6 ++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/public/src/client/chats.js b/public/src/client/chats.js index e853bffec1..021d548889 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -68,7 +68,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', }) .on('click', '[data-action="delete"]', function() { var messageId = $(this).parents('[data-mid]').attr('data-mid'); - Chats.delete(messageId); + Chats.delete(messageId, ajaxify.data.roomId); }); $('.recent-chats').on('scroll', function() { @@ -126,7 +126,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', }); }; - Chats.delete = function(messageId) { + Chats.delete = function(messageId, roomId) { translator.translate('[[modules:chat.delete_message_confirm]]', function(translated) { bootbox.confirm(translated, function(ok) { if (!ok) { @@ -134,7 +134,8 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', } socket.emit('modules.chats.delete', { - messageId: messageId + messageId: messageId, + roomId: roomId }, function(err) { if (err) { return app.alertError(err.message); @@ -366,6 +367,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', }); } else { socket.emit('modules.chats.edit', { + roomId: roomId, mid: mid, message: msg }, function(err) { diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index 57d5811de2..a3d4620f08 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -260,7 +260,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra }) .on('click', '[data-action="delete"]', function() { var messageId = $(this).parents('[data-mid]').attr('data-mid'); - Chats.delete(messageId); + Chats.delete(messageId, data.roomId); }); Chats.addSinceHandler(chatModal.attr('roomId'), chatModal.find('.chat-content'), chatModal.find('[data-since]')); diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index 3f22f19500..3cd7ba3486 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -174,9 +174,11 @@ SocketModules.chats.delete = function(socket, data, callback) { } Messaging.canEdit(data.messageId, socket.uid, function(err, allowed) { - if (allowed) { - Messaging.deleteMessage(data.messageId, data.roomId, callback); + if (err || !allowed) { + return callback(err || new Error('[[error:cant-delete-chat-message]]')); } + + Messaging.deleteMessage(data.messageId, data.roomId, callback); }); }; From 5a26eb1c7677cca953403459ef84e296495323f2 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 17 Dec 2015 12:25:30 +0200 Subject: [PATCH 25/27] IS fix, timeago fix --- public/src/client/chats.js | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/public/src/client/chats.js b/public/src/client/chats.js index 021d548889..be9b76a050 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -71,7 +71,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', Chats.delete(messageId, ajaxify.data.roomId); }); - $('.recent-chats').on('scroll', function() { + $('[component="chat/recent"]').on('scroll', function() { var $this = $(this); var bottom = ($this[0].scrollHeight - $this.height()) * 0.9; if ($this.scrollTop() > bottom) { @@ -406,7 +406,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', }; function loadMoreRecentChats() { - var recentChats = $('.recent-chats'); + var recentChats = $('[component="chat/recent"]'); if (recentChats.attr('loading')) { return; } @@ -418,8 +418,8 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', return app.alertError(err.message); } - if (data && data.users.length) { - onRecentChatsLoaded(data.users, function() { + if (data && data.rooms.length) { + onRecentChatsLoaded(data, function() { recentChats.removeAttr('loading'); recentChats.attr('data-nextstart', data.nextStart); }); @@ -429,17 +429,14 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', }); } - function onRecentChatsLoaded(users, callback) { - users = users.filter(function(user) { - return !$('.recent-chats li[data-uid=' + user.uid + ']').length; - }); - - if (!users.length) { + function onRecentChatsLoaded(data, callback) { + if (!data.rooms.length) { return callback(); } - app.parseAndTranslate('chats', 'chats', {chats: users}, function(html) { - $('.recent-chats').append(html); + app.parseAndTranslate('chats', 'rooms', data, function(html) { + $('[component="chat/recent"]').append(html); + html.find('.timeago').timeago(); callback(); }); } From 4827ea04fcd1c7d2b7fc865c95f0f268e14c7de9 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 17 Dec 2015 12:35:49 +0200 Subject: [PATCH 26/27] messaging.getTeaser --- src/messaging.js | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/messaging.js b/src/messaging.js index eb254a235b..c7c8f26978 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -254,19 +254,7 @@ var async = require('async'), }, teasers: function(next) { async.map(roomIds, function(roomId, next) { - Messaging.getMessages({ - uid: uid, - roomId: roomId, - isNew: false, - count: 1, - markRead: false - }, function(err, teaser) { - teaser = teaser[0]; - if (teaser && teaser.content) { - teaser.content = S(teaser.content).stripTags().decodeHTMLEntities().s; - } - next(err, teaser); - }); + Messaging.getTeaser(uid, roomId, next); }, next); } }, function(err, results) { @@ -300,6 +288,28 @@ var async = require('async'), }); }; + Messaging.getTeaser = function (uid, roomId, callback) { + async.waterfall([ + function (next) { + db.getSortedSetRevRange('uid:' + uid + ':chat:room:' + roomId + ':mids', 0, 0, next); + }, + function (mids, next) { + if (!mids || !mids.length) { + return next(null, null); + } + Messaging.getMessageFields(mids[0], ['content', 'timestamp'], next); + }, + function (teaser, next) { + if (teaser && teaser.content) { + teaser.content = S(teaser.content).stripTags().decodeHTMLEntities().s; + teaser.timestampISO = utils.toISOString(teaser.timestamp); + } + + next(null, teaser); + } + ], callback); + }; + Messaging.canMessageUser = function(uid, toUid, callback) { if (parseInt(meta.config.disableChat) === 1 || !uid || uid === toUid) { return callback(null, false); From 351bf6c327d5bd8cccdf48ca63cd9b2e936d9af0 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 18 Dec 2015 13:19:16 +0200 Subject: [PATCH 27/27] fix tests --- tests/messaging.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/messaging.js b/tests/messaging.js index a709640430..ae1db0952a 100644 --- a/tests/messaging.js +++ b/tests/messaging.js @@ -27,14 +27,14 @@ describe('Messaging Library', function() { describe('.canMessage()', function() { it('should not error out', function(done) { - Messaging.canMessage(testUids[1], testUids[2], function(err, allowed) { + Messaging.canMessageUser(testUids[1], testUids[2], function(err, allowed) { assert.ifError(err); done(); }); }); it('should allow messages to be sent to an unrestricted user', function(done) { - Messaging.canMessage(testUids[1], testUids[2], function(err, allowed) { + Messaging.canMessageUser(testUids[1], testUids[2], function(err, allowed) { assert.strictEqual(allowed, true, 'should be true, received ' + allowed); done(); }); @@ -42,7 +42,7 @@ describe('Messaging Library', function() { it('should NOT allow messages to be sent to a restricted user', function(done) { User.setSetting(testUids[1], 'restrictChat', '1', function() { - Messaging.canMessage(testUids[2], testUids[1], function(err, allowed) { + Messaging.canMessageUser(testUids[2], testUids[1], function(err, allowed) { assert.strictEqual(allowed, false, 'should be false, received ' + allowed); done(); }); @@ -50,7 +50,7 @@ describe('Messaging Library', function() { }); it('should always allow admins through', function(done) { - Messaging.canMessage(testUids[0], testUids[1], function(err, allowed) { + Messaging.canMessageUser(testUids[0], testUids[1], function(err, allowed) { assert.strictEqual(allowed, true, 'should be true, received ' + allowed); done(); }); @@ -58,7 +58,7 @@ describe('Messaging Library', function() { it('should allow messages to be sent to a restricted user if restricted user follows sender', function(done) { User.follow(testUids[1], testUids[2], function() { - Messaging.canMessage(testUids[2], testUids[1], function(err, allowed) { + Messaging.canMessageUser(testUids[2], testUids[1], function(err, allowed) { assert.strictEqual(allowed, true, 'should be true, received ' + allowed); done(); });