diff --git a/public/language/en_GB/modules.json b/public/language/en_GB/modules.json index 5dc56fa8c2..a819610819 100644 --- a/public/language/en_GB/modules.json +++ b/public/language/en_GB/modules.json @@ -16,6 +16,7 @@ "chat.thirty_days": "30 Days", "chat.three_months": "3 Months", "chat.delete_message_confirm": "Are you sure you wish to delete this message?", + "chat.roomname": "Chat Room %1", "composer.compose": "Compose", "composer.show_preview": "Show Preview", diff --git a/public/src/app.js b/public/src/app.js index a808c34ee1..ddf8dd6d65 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -261,11 +261,7 @@ app.cacheBuster = null; roomData.users = roomData.users.filter(function(user) { return user && parseInt(user.uid, 10) !== parseInt(app.user.uid, 10); }); - chat.createModal({ - roomId: roomId, - users: roomData.users, - owner: roomData.owner - }, loadAndCenter); + chat.createModal(roomData, loadAndCenter); }); } }); diff --git a/public/src/client/chats.js b/public/src/client/chats.js index 8be8c4203a..9d1c9e348f 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -80,6 +80,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', }); Chats.addSinceHandler(ajaxify.data.roomId, $('.expanded-chat .chat-content'), $('.expanded-chat [data-since]')); + Chats.addRenameHandler(ajaxify.data.roomId, $('[component="chat/room/name"]')); }; Chats.addHotkeys = function() { @@ -159,6 +160,27 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', }); }; + Chats.addRenameHandler = function(roomId, inputEl) { + var oldName = inputEl.val(); + inputEl.on('blur keypress', function(ev) { + if (ev.type === 'keypress' && ev.keyCode !== 13) { + return; + } + var newName = inputEl.val(); + + if (oldName === newName) { + return; + } + socket.emit('modules.chats.renameRoom', {roomId: roomId, newName: newName}, function(err) { + if (err) { + return app.alertError(err.message); + } + oldName = newName; + inputEl.blur(); + }); + }); + }; + Chats.addSendHandlers = function(roomId, inputEl, sendEl) { inputEl.off('keypress').on('keypress', function(e) { @@ -209,7 +231,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', }); tagEl.on('beforeItemRemove', function(event) { - event.cancel = !data.owner; + event.cancel = !data.isOwner; }); tagEl.on('itemRemoved', function(event) { @@ -309,6 +331,10 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', }); }); }); + + socket.on('event:chats.roomRename', function(data) { + $('[component="chat/room/name"]').val(data.newName); + }); }; Chats.resizeMainWindow = function() { diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index 08c84f3ca5..2414052b3b 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -53,12 +53,8 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra roomData.users = roomData.users.filter(function(user) { return user && parseInt(user.uid, 10) !== parseInt(app.user.uid, 10); }); - module.createModal({ - roomId: data.roomId, - users: roomData.users, - owner: roomData.owner, - silent: true - }, function(modal) { + roomData.silent = true; + module.createModal(roomData, function(modal) { module.toggleNew(modal.attr('UUID'), true, true); if (!isSelf) { app.alternatingTitle('[[modules:chat.user_has_messaged_you, ' + username + ']]'); @@ -92,6 +88,10 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra var modal = module.getModal(data.uid); app.updateUserStatus(modal.find('[component="user/status"]'), data.status); }); + + socket.on('event:chats.roomRename', function(data) { + module.getModal(data.roomId).find('[component="chat/room/name"]').val(data.newName); + }); }; module.loadChatsDropdown = function(chatsListEl) { @@ -182,7 +182,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra } module.createModal = function(data, callback) { - templates.parse('chat', {}, function(chatTpl) { + templates.parse('chat', data, function(chatTpl) { translator.translate(chatTpl, function (chatTpl) { var chatModal = $(chatTpl), @@ -274,6 +274,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra }); Chats.addSinceHandler(chatModal.attr('roomId'), chatModal.find('.chat-content'), chatModal.find('[data-since]')); + Chats.addRenameHandler(chatModal.attr('roomId'), chatModal.find('[component="chat/room/name"]')); Chats.addSendHandlers(chatModal.attr('roomId'), chatModal.find('#chat-message-input'), chatModal.find('#chat-message-send-btn')); diff --git a/src/controllers/accounts/chats.js b/src/controllers/accounts/chats.js index 49dd5091da..9b5342f7ab 100644 --- a/src/controllers/accounts/chats.js +++ b/src/controllers/accounts/chats.js @@ -46,30 +46,32 @@ chatsController.get = function(req, res, callback) { since: 'recent', isNew: false }), - owner: async.apply(messaging.isRoomOwner, req.uid, req.params.roomid) + room: async.apply(messaging.getRoomData, req.params.roomid) }, next); } ], function(err, data) { if (err) { return callback(err); } + var room = data.room; + room.messages = data.messages; - data.users = data.users.filter(function(user) { + room.isOwner = parseInt(room.owner, 10) === parseInt(req.uid, 10); + room.users = data.users.filter(function(user) { return user && parseInt(user.uid, 10) !== req.uid; }); - data.usernames = data.users.map(function(user) { + room.usernames = data.users.map(function(user) { return user && user.username; }).join(', '); - 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.roomId}]); + room.rooms = recentChats.rooms; + room.nextStart = recentChats.nextStart; + room.title = room.roomName; + room.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[pages:chats]]', url: '/chats'}, {text: room.roomName}]); - res.render('chats', data); + res.render('chats', room); }); }); }; diff --git a/src/messaging/rooms.js b/src/messaging/rooms.js index 54f67796ee..35d9af4172 100644 --- a/src/messaging/rooms.js +++ b/src/messaging/rooms.js @@ -1,12 +1,26 @@ 'use strict'; var async = require('async'); +var validator = require('validator'); var db = require('../database'); var user = require('../user'); module.exports = function(Messaging) { + Messaging.getRoomData = function(roomId, callback) { + db.getObject('chat:room:' + roomId, function(err, data) { + if (err || !data) { + return callback(err || new Error('[[error:no-chat-room]]')); + } + data.roomName = data.roomName || '[[modules:chat.roomname, ' + roomId + ']]'; + if (data.roomName) { + data.roomName = validator.escape(data.roomName); + } + callback(null, data); + }); + }; + Messaging.newRoom = function(uid, toUids, callback) { var roomId; var now = Date.now(); @@ -105,4 +119,22 @@ module.exports = function(Messaging) { ], callback); }; + Messaging.renameRoom = function(uid, roomId, newName, callback) { + if (!newName) { + return callback(new Error('[[error:invalid-name]]')); + } + + async.waterfall([ + function (next) { + Messaging.isRoomOwner(uid, roomId, next); + }, + function (isOwner, next) { + if (!isOwner) { + return next(new Error('[[error:no-privileges]]')); + } + db.setObjectField('chat:room:' + roomId, 'roomName', newName, next); + } + ], callback); + }; + }; \ No newline at end of file diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index 1467e13247..6e7df97d52 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -1,6 +1,8 @@ "use strict"; var async = require('async'); +var validator = require('validator'); + var meta = require('../meta'); var Messaging = require('../messaging'); var utils = require('../../public/src/utils'); @@ -105,23 +107,25 @@ SocketModules.chats.loadRoom = function(socket, data, callback) { if (!data || !data.roomId) { return callback(new Error('[[error:invalid-data]]')); } - var isOwner = false; + async.waterfall([ function (next) { + Messaging.isUserInRoom(socket.uid, data.roomId, next); + }, + function (inRoom, next) { + if (!inRoom) { + return next(new Error('[[error:not-allowed]]')); + } + async.parallel({ - inRoom: async.apply(Messaging.isUserInRoom, socket.uid, data.roomId), - isOwner: async.apply(Messaging.isRoomOwner, socket.uid, data.roomId) + roomData: async.apply(Messaging.getRoomData, data.roomId), + users: async.apply(Messaging.getUsersInRoom, data.roomId, 0, -1) }, next); }, function (results, next) { - if (!results.inRoom) { - return next(new Error('[[error:not-allowerd]]')); - } - isOwner = results.isOwner; - Messaging.getUsersInRoom(data.roomId, 0, -1, next); - }, - function (users, next) { - next(null, {users: users, owner: isOwner}); + results.roomData.users = results.users; + results.roomData.isOwner = parseInt(results.roomData.owner, 10) === socket.uid; + next(null, results.roomData); } ], callback); }; @@ -209,6 +213,28 @@ SocketModules.chats.markRead = function(socket, roomId, callback) { }); }; +SocketModules.chats.renameRoom = function(socket, data, callback) { + if (!data) { + return callback(new Error('[[error:invalid-name]]')); + } + + async.waterfall([ + function (next) { + Messaging.renameRoom(socket.uid, data.roomId, data.newName, next); + }, + function (next) { + Messaging.getUidsInRoom(data.roomId, 0, -1, next); + }, + function (uids, next) { + var eventData = {roomId: data.roomId, newName: validator.escape(data.newName)}; + uids.forEach(function(uid) { + server.in('uid_' + uid).emit('event:chats.roomRename', eventData); + }); + next(); + } + ], callback); +}; + SocketModules.chats.userStartTyping = function(socket, data, callback) { sendTypingNotification('event:chats.userStartTyping', socket, data, callback); }; diff --git a/src/upgrade.js b/src/upgrade.js index e23a260cbe..94aeb1ea55 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -161,7 +161,7 @@ Upgrade.upgrade = function(callback) { var currentMid = 1; async.whilst(function() { - return currentMid < globalData.nextMid; + return currentMid <= globalData.nextMid; }, function(next) { db.getObject('message:' + currentMid, function(err, message) { function addMessageToUids(roomId, callback) { @@ -246,7 +246,7 @@ Upgrade.upgrade = function(callback) { } var currentChatRoomId = 1; async.whilst(function() { - return currentChatRoomId < nextChatRoomId; + return currentChatRoomId <= nextChatRoomId; }, function(next) { db.getSortedSetRange('chat:room:' + currentChatRoomId + ':uids', 0, 0, function(err, uids) { if (err) {