From 7c798f57e89be2576a3da4966ce19533ba918e45 Mon Sep 17 00:00:00 2001 From: Renato Becker Date: Mon, 9 Sep 2019 20:23:10 -0300 Subject: [PATCH] Regression: New Livechat methods and processes (#15242) --- .../client/views/app/tabbar/visitorEdit.js | 8 +- .../client/views/app/tabbar/visitorInfo.js | 14 +-- app/livechat/lib/LivechatInquiry.js | 39 ++------- app/livechat/server/api/v1/room.js | 2 +- app/livechat/server/config.js | 8 +- app/livechat/server/index.js | 4 +- app/livechat/server/lib/Helper.js | 86 +++++++++++-------- app/livechat/server/lib/Livechat.js | 16 ++-- app/livechat/server/lib/RoutingManager.js | 35 +++++--- .../{LeastAmount.js => AutoSelection.js} | 6 +- .../{GuestPool.js => ManualSelection.js} | 12 ++- .../server/publications/livechatAgents.js | 2 +- .../server/publications/livechatManagers.js | 2 +- .../server/publications/livechatRooms.js | 17 +++- app/livechat/server/startup.js | 9 ++ app/models/server/models/LivechatRooms.js | 16 ---- app/models/server/models/Settings.js | 34 ++++++++ packages/rocketchat-i18n/i18n/en.i18n.json | 6 +- packages/rocketchat-i18n/i18n/pt-BR.i18n.json | 4 +- server/startup/migrations/index.js | 1 + server/startup/migrations/v155.js | 34 ++++++++ 21 files changed, 215 insertions(+), 140 deletions(-) rename app/livechat/server/lib/routing/{LeastAmount.js => AutoSelection.js} (84%) rename app/livechat/server/lib/routing/{GuestPool.js => ManualSelection.js} (86%) create mode 100644 server/startup/migrations/v155.js diff --git a/app/livechat/client/views/app/tabbar/visitorEdit.js b/app/livechat/client/views/app/tabbar/visitorEdit.js index 60c902855f1..3a782f39305 100644 --- a/app/livechat/client/views/app/tabbar/visitorEdit.js +++ b/app/livechat/client/views/app/tabbar/visitorEdit.js @@ -7,7 +7,7 @@ import { t } from '../../../../../utils'; import { hasRole } from '../../../../../authorization'; import { LivechatVisitor } from '../../../collections/LivechatVisitor'; import { LivechatDepartmentAgents } from '../../../collections/LivechatDepartmentAgents'; -import { Rooms } from '../../../../../models'; +import { LivechatRoom } from '../../../collections/LivechatRoom'; import './visitorEdit.html'; Template.visitorEdit.helpers({ @@ -79,9 +79,11 @@ Template.visitorEdit.onCreated(function() { this.visitor.set(LivechatVisitor.findOne({ _id: Template.currentData().visitorId })); }); - this.autorun(() => { - const room = Rooms.findOne({ _id: Template.currentData().roomId }); + const rid = Template.currentData().roomId; + this.subscribe('livechat:rooms', { _id: rid }); + this.autorun(() => { + const room = LivechatRoom.findOne({ _id: rid }); this.room.set(room); this.tags.set((room && room.tags) || []); }); diff --git a/app/livechat/client/views/app/tabbar/visitorInfo.js b/app/livechat/client/views/app/tabbar/visitorInfo.js index abc87e3c98c..00df07ef77c 100644 --- a/app/livechat/client/views/app/tabbar/visitorInfo.js +++ b/app/livechat/client/views/app/tabbar/visitorInfo.js @@ -10,12 +10,13 @@ import moment from 'moment'; import UAParser from 'ua-parser-js'; import { modal } from '../../../../../ui-utils'; -import { Rooms, Subscriptions } from '../../../../../models'; +import { Subscriptions } from '../../../../../models'; import { settings } from '../../../../../settings'; import { t, handleError, roomTypes } from '../../../../../utils'; import { hasRole, hasAllPermission, hasAtLeastOnePermission } from '../../../../../authorization'; import { LivechatVisitor } from '../../../collections/LivechatVisitor'; import { LivechatDepartment } from '../../../collections/LivechatDepartment'; +import { LivechatRoom } from '../../../collections/LivechatRoom'; import './visitorInfo.html'; const isSubscribedToRoom = () => { @@ -50,7 +51,7 @@ Template.visitorInfo.helpers({ }, room() { - return Template.instance().room.get(); + return LivechatRoom.findOne({ _id: this.rid }); }, department() { @@ -72,7 +73,7 @@ Template.visitorInfo.helpers({ const data = Template.currentData(); if (data && data.rid) { - const room = Rooms.findOne(data.rid); + const room = LivechatRoom.findOne(data.rid); if (room) { livechatData = _.extend(livechatData, room.livechatData); } @@ -147,7 +148,7 @@ Template.visitorInfo.helpers({ }, roomOpen() { - const room = Template.instance().room.get(); + const room = LivechatRoom.findOne({ _id: this.rid }); const uid = Meteor.userId(); return room && room.open && ((room.servedBy && room.servedBy._id === uid) || hasRole(uid, 'livechat-manager')); }, @@ -281,7 +282,6 @@ Template.visitorInfo.onCreated(function() { this.user = new ReactiveVar(); this.departmentId = new ReactiveVar(null); this.tags = new ReactiveVar(null); - this.room = new ReactiveVar(null); this.routingConfig = new ReactiveVar({}); Meteor.call('livechat:getCustomFields', (err, customFields) => { @@ -298,9 +298,9 @@ Template.visitorInfo.onCreated(function() { }); if (rid) { + this.subscribe('livechat:rooms', { _id: rid }); this.autorun(() => { - const room = Rooms.findOne({ _id: rid }); - this.room.set(room); + const room = LivechatRoom.findOne({ _id: rid }); this.visitorId.set(room && room.v && room.v._id); this.departmentId.set(room && room.departmentId); this.tags.set(room && room.tags); diff --git a/app/livechat/lib/LivechatInquiry.js b/app/livechat/lib/LivechatInquiry.js index 43d57b229b1..af655c339c4 100644 --- a/app/livechat/lib/LivechatInquiry.js +++ b/app/livechat/lib/LivechatInquiry.js @@ -18,7 +18,6 @@ if (Meteor.isServer) { this.tryEnsureIndex({ name: 1 }); // name of the inquiry (client name for now) this.tryEnsureIndex({ message: 1 }); // message sent by the client this.tryEnsureIndex({ ts: 1 }); // timestamp - this.tryEnsureIndex({ agents: 1 }); // Id's of the agents who can see the inquiry (handle departments) this.tryEnsureIndex({ department: 1 }); this.tryEnsureIndex({ status: 1 }); // 'ready', 'queued', 'taken' } @@ -56,23 +55,6 @@ if (Meteor.isServer) { }); } - /* - * mark the inquiry as closed - */ - closeByRoomId(roomId, closeInfo) { - return this.update({ - rid: roomId, - }, { - $set: { - status: 'closed', - closer: closeInfo.closer, - closedBy: closeInfo.closedBy, - closedAt: closeInfo.closedAt, - 'metrics.chatDuration': closeInfo.chatDuration, - }, - }); - } - /* * mark inquiry as open */ @@ -95,20 +77,6 @@ if (Meteor.isServer) { }); } - /* - * mark inquiry as open and set agents - */ - queueInquiryWithAgents(inquiryId, agentIds) { - return this.update({ - _id: inquiryId, - }, { - $set: { - status: 'queued', - agents: agentIds, - }, - }); - } - changeDepartmentIdByRoomId(rid, department) { const query = { rid, @@ -199,6 +167,13 @@ if (Meteor.isServer) { return collectionObj.aggregate(aggregate).toArray(); } + + /* + * remove the inquiry by roomId + */ + removeByRoomId(rid) { + return this.remove({ rid }); + } } LivechatInquiry = new LivechatInquiryClass(); diff --git a/app/livechat/server/api/v1/room.js b/app/livechat/server/api/v1/room.js index 067138d929e..3ebce3ec482 100644 --- a/app/livechat/server/api/v1/room.js +++ b/app/livechat/server/api/v1/room.js @@ -103,7 +103,7 @@ API.v1.addRoute('livechat/room.transfer', { // update visited page history to not expire Messages.keepHistoryForToken(token); - if (!Livechat.transfer(room, guest, { roomId: rid, departmentId: department })) { + if (!Promise.await(Livechat.transfer(room, guest, { roomId: rid, departmentId: department }))) { return API.v1.failure(); } diff --git a/app/livechat/server/config.js b/app/livechat/server/config.js index 3fb0656b4b8..0dfb843f32f 100644 --- a/app/livechat/server/config.js +++ b/app/livechat/server/config.js @@ -362,15 +362,15 @@ Meteor.startup(function() { i18nLabel: 'RDStation_Token', }); - settings.add('Livechat_Routing_Method', 'Least_Amount', { + settings.add('Livechat_Routing_Method', 'Auto_Selection', { type: 'select', group: 'Livechat', public: true, section: 'Routing', values: [ { key: 'External', i18nLabel: 'External_Service' }, - { key: 'Least_Amount', i18nLabel: 'Least_Amount' }, - { key: 'Guest_Pool', i18nLabel: 'Guest_Pool' }, + { key: 'Auto_Selection', i18nLabel: 'Auto_Selection' }, + { key: 'Manual_Selection', i18nLabel: 'Manual_Selection' }, ], }); @@ -388,7 +388,7 @@ Meteor.startup(function() { section: 'Routing', i18nLabel: 'Max_number_incoming_livechats_displayed', i18nDescription: 'Max_number_incoming_livechats_displayed_description', - enableQuery: { _id: 'Livechat_Routing_Method', value: 'Guest_Pool' }, + enableQuery: { _id: 'Livechat_Routing_Method', value: 'Manual_Selection' }, }); settings.add('Livechat_show_queue_list_link', false, { diff --git a/app/livechat/server/index.js b/app/livechat/server/index.js index 3c937b9e1f6..14388868db7 100644 --- a/app/livechat/server/index.js +++ b/app/livechat/server/index.js @@ -70,8 +70,8 @@ import './lib/QueueManager'; import './lib/OfficeClock'; import './lib/RoutingManager'; import './lib/routing/External'; -import './lib/routing/GuestPool'; -import './lib/routing/LeastAmount'; +import './lib/routing/ManualSelection'; +import './lib/routing/AutoSelection'; import './sendMessageBySMS'; import './unclosedLivechats'; import './publications/customFields'; diff --git a/app/livechat/server/lib/Helper.js b/app/livechat/server/lib/Helper.js index 89025b56499..7e47cb621f5 100644 --- a/app/livechat/server/lib/Helper.js +++ b/app/livechat/server/lib/Helper.js @@ -6,6 +6,7 @@ import { Messages, LivechatRooms, Rooms, Subscriptions, Users } from '../../../m import { LivechatInquiry } from '../../lib/LivechatInquiry'; import { Livechat } from './Livechat'; import { RoutingManager } from './RoutingManager'; +import { callbacks } from '../../../callbacks/server'; export const createLivechatRoom = (rid, name, guest, extraData) => { check(rid, String); @@ -39,7 +40,9 @@ export const createLivechatRoom = (rid, name, guest, extraData) => { waitingResponse: true, }, extraData); - return Rooms.insert(room); + const roomId = Rooms.insert(room); + callbacks.run('livechat.newRoom', room); + return roomId; }; export const createLivechatInquiry = (rid, name, guest, message, initialStatus) => { @@ -146,6 +149,11 @@ export const createLivechatQueueView = () => { }); }; +export const removeAgentFromSubscription = (rid, { _id, username }) => { + Subscriptions.removeByRoomIdAndUserId(rid, _id); + Messages.createUserLeaveWithRoomIdAndUser(rid, { _id, username }); +}; + export const dispatchAgentDelegated = (rid, agentId) => { const agent = agentId && Users.getAgentInfo(agentId); Livechat.stream.emit(rid, { @@ -155,6 +163,10 @@ export const dispatchAgentDelegated = (rid, agentId) => { }; export const forwardRoomToAgent = async (room, agentId) => { + if (!room || !room.open) { + return false; + } + const user = Users.findOneOnlineAgentById(agentId); if (!user) { return false; @@ -166,31 +178,36 @@ export const forwardRoomToAgent = async (room, agentId) => { throw new Meteor.Error('error-transferring-inquiry'); } + if (oldServedBy && agentId === oldServedBy._id) { + return false; + } const { username } = user; const agent = { agentId, username }; + // There are some Enterprise features that may interrupt the fowarding process + // Due to that we need to check whether the agent has been changed or not + const roomTaken = await RoutingManager.takeInquiry(inquiry, agent); + if (!roomTaken) { + return false; + } - if (oldServedBy && agent.agentId !== oldServedBy._id) { - // There are some Enterprise features that may interrupt the fowarding process - // Due to that we need to check whether the agent has been changed or not - const room = await RoutingManager.takeInquiry(inquiry, agent); - if (!room) { - return false; + const { servedBy } = roomTaken; + if (servedBy) { + if (oldServedBy && servedBy._id !== oldServedBy._id) { + removeAgentFromSubscription(rid, oldServedBy); } - const { servedBy } = room; - if (servedBy && servedBy._id !== oldServedBy._id) { - Subscriptions.removeByRoomIdAndUserId(rid, oldServedBy._id); - Messages.createUserLeaveWithRoomIdAndUser(rid, { _id: oldServedBy._id, username: oldServedBy.username }); - Messages.createUserJoinWithRoomIdAndUser(rid, { _id: agent.agentId, username }); - return true; - } + Messages.createUserJoinWithRoomIdAndUser(rid, { _id: servedBy._id, username: servedBy.username }); } - return false; + return true; }; export const forwardRoomToDepartment = async (room, guest, departmentId) => { + if (!room || !room.open) { + return false; + } + const { _id: rid, servedBy: oldServedBy } = room; const inquiry = LivechatInquiry.findOneByRoomId(rid); @@ -205,30 +222,29 @@ export const forwardRoomToDepartment = async (room, guest, departmentId) => { // Fake the department to forward the inquiry - Case the forward process does not success // the inquiry will stay in the same original department inquiry.department = departmentId; - room = await RoutingManager.delegateInquiry(inquiry); - if (!room) { + const roomTaken = await RoutingManager.delegateInquiry(inquiry); + if (!roomTaken) { return false; } - const { servedBy } = room; - // if there was an agent assigned to the chat and there is no new agent assigned - // or the new agent is not the same, then the fowarding process successed - if (oldServedBy && (!servedBy || oldServedBy._id !== servedBy._id)) { - Subscriptions.removeByRoomIdAndUserId(rid, oldServedBy._id); - Messages.createUserLeaveWithRoomIdAndUser(rid, { _id: oldServedBy._id, username: oldServedBy.username }); - LivechatRooms.changeDepartmentIdByRoomId(rid, departmentId); - LivechatInquiry.changeDepartmentIdByRoomId(rid, departmentId); - // Update the visitor's department - const { token } = guest; - Livechat.setDepartmentForGuest({ token, department: departmentId }); - - if (servedBy) { - const { _id, username } = servedBy; - Messages.createUserJoinWithRoomIdAndUser(rid, { _id, username }); - } + const { servedBy } = roomTaken; + if (oldServedBy && servedBy && oldServedBy._id === servedBy._id) { + return false; + } + + if (oldServedBy) { + removeAgentFromSubscription(rid, oldServedBy); + } - return true; + if (servedBy) { + Messages.createUserJoinWithRoomIdAndUser(rid, servedBy); } - return false; + LivechatRooms.changeDepartmentIdByRoomId(rid, departmentId); + LivechatInquiry.changeDepartmentIdByRoomId(rid, departmentId); + + const { token } = guest; + Livechat.setDepartmentForGuest({ token, department: departmentId }); + + return true; }; diff --git a/app/livechat/server/lib/Livechat.js b/app/livechat/server/lib/Livechat.js index d379b8682f8..908b5b25ad2 100644 --- a/app/livechat/server/lib/Livechat.js +++ b/app/livechat/server/lib/Livechat.js @@ -21,6 +21,7 @@ import { Messages, Subscriptions, Settings, + Rooms, LivechatDepartmentAgents, LivechatDepartment, LivechatCustomField, @@ -294,8 +295,9 @@ export const Livechat = { }; } - LivechatRooms.closeByRoomId(room._id, closeData); - LivechatInquiry.closeByRoomId(room._id, closeData); + const { _id: rid, servedBy } = room; + LivechatRooms.closeByRoomId(rid, closeData); + LivechatInquiry.removeByRoomId(rid); const message = { t: 'livechat-close', @@ -304,14 +306,14 @@ export const Livechat = { }; // Retreive the closed room - room = LivechatRooms.findOneByIdOrName(room._id); + room = LivechatRooms.findOneByIdOrName(rid); sendMessage(user, message, room); - if (room.servedBy) { - Subscriptions.hideByRoomIdAndUserId(room._id, room.servedBy._id); + if (servedBy) { + Subscriptions.hideByRoomIdAndUserId(rid, servedBy._id); } - Messages.createCommandWithRoomIdAndUser('promptTranscript', room._id, closeData.closedBy); + Messages.createCommandWithRoomIdAndUser('promptTranscript', rid, closeData.closedBy); Meteor.defer(() => { callbacks.run('livechat.closeRoom', room); @@ -388,7 +390,7 @@ export const Livechat = { }); if (!_.isEmpty(guestData.name)) { - return LivechatRooms.setNameById(roomData._id, guestData.name, guestData.name) && Subscriptions.updateDisplayNameByRoomId(roomData._id, guestData.name); + return Rooms.setFnameById(roomData._id, guestData.name) && Subscriptions.updateDisplayNameByRoomId(roomData._id, guestData.name); } }, diff --git a/app/livechat/server/lib/RoutingManager.js b/app/livechat/server/lib/RoutingManager.js index 28c834bcd32..8a218a88c47 100644 --- a/app/livechat/server/lib/RoutingManager.js +++ b/app/livechat/server/lib/RoutingManager.js @@ -2,9 +2,14 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import { settings } from '../../../settings/server'; -import { createLivechatSubscription, dispatchAgentDelegated, forwardRoomToAgent, forwardRoomToDepartment } from './Helper'; +import { createLivechatSubscription, + dispatchAgentDelegated, + forwardRoomToAgent, + forwardRoomToDepartment, + removeAgentFromSubscription, +} from './Helper'; import { callbacks } from '../../../callbacks/server'; -import { LivechatRooms, Rooms, Messages, Subscriptions, Users } from '../../../models/server'; +import { LivechatRooms, Rooms, Messages, Users } from '../../../models/server'; import { LivechatInquiry } from '../../lib/LivechatInquiry'; export const RoutingManager = { @@ -68,16 +73,11 @@ export const RoutingManager = { unassignAgent(inquiry, departmentId) { const { _id, rid, department } = inquiry; const room = LivechatRooms.findOneById(rid); - const { servedBy } = room; - if (!servedBy) { + if (!room || !room.open) { return false; } - Subscriptions.removeByRoomId(rid); - LivechatRooms.removeAgentByRoomId(rid); - LivechatInquiry.queueInquiry(_id); - if (departmentId && departmentId !== department) { LivechatRooms.changeDepartmentIdByRoomId(rid, departmentId); LivechatInquiry.changeDepartmentIdByRoomId(rid, departmentId); @@ -85,15 +85,19 @@ export const RoutingManager = { inquiry.department = departmentId; } - this.getMethod().delegateAgent(null, inquiry); - Messages.createUserLeaveWithRoomIdAndUser(rid, { _id: servedBy._id, username: servedBy.username }); - dispatchAgentDelegated(rid, null); + const { servedBy } = room; + if (servedBy) { + removeAgentFromSubscription(rid, servedBy); + LivechatRooms.removeAgentByRoomId(rid); + dispatchAgentDelegated(rid, null); + } + LivechatInquiry.queueInquiry(_id); + this.getMethod().delegateAgent(null, inquiry); return true; }, async takeInquiry(inquiry, agent) { - // return Room Object check(agent, Match.ObjectIncluding({ agentId: String, username: String, @@ -107,7 +111,11 @@ export const RoutingManager = { const { _id, rid } = inquiry; const room = LivechatRooms.findOneById(rid); - if (room && room.servedBy && room.servedBy._id === agent.agentId) { + if (!room || !room.open) { + return room; + } + + if (room.servedBy && room.servedBy._id === agent.agentId) { return room; } @@ -126,7 +134,6 @@ export const RoutingManager = { async transferRoom(room, guest, transferData) { const { userId, departmentId } = transferData; - if (userId) { return forwardRoomToAgent(room, userId); } diff --git a/app/livechat/server/lib/routing/LeastAmount.js b/app/livechat/server/lib/routing/AutoSelection.js similarity index 84% rename from app/livechat/server/lib/routing/LeastAmount.js rename to app/livechat/server/lib/routing/AutoSelection.js index c52f0d41c08..4157159f169 100644 --- a/app/livechat/server/lib/routing/LeastAmount.js +++ b/app/livechat/server/lib/routing/AutoSelection.js @@ -1,12 +1,12 @@ import { RoutingManager } from '../RoutingManager'; import { LivechatDepartmentAgents, Users } from '../../../../models/server'; -/* Least Amount Queuing method: +/* Auto Selection Queuing method: * * default method where the agent with the least number * of open chats is paired with the incoming livechat */ -class LeastAmount { +class AutoSelection { constructor() { this.config = { previewRoom: false, @@ -32,4 +32,4 @@ class LeastAmount { } } -RoutingManager.registerMethod('Least_Amount', LeastAmount); +RoutingManager.registerMethod('Auto_Selection', AutoSelection); diff --git a/app/livechat/server/lib/routing/GuestPool.js b/app/livechat/server/lib/routing/ManualSelection.js similarity index 86% rename from app/livechat/server/lib/routing/GuestPool.js rename to app/livechat/server/lib/routing/ManualSelection.js index a4838542b68..ebf294d091f 100644 --- a/app/livechat/server/lib/routing/GuestPool.js +++ b/app/livechat/server/lib/routing/ManualSelection.js @@ -7,16 +7,16 @@ import { LivechatInquiry } from '../../../lib/LivechatInquiry'; import { sendNotification } from '../../../../lib/server'; import { LivechatRooms } from '../../../../models/server'; -/* Guest Pool Queuing Method: +/* Manual Selection Queuing Method: * * An incomming livechat is created as an Inquiry * which is picked up from an agent. - * An Inquiry is visible to all agents (TODO: in the correct department) + * An Inquiry is visible to all agents * * A room is still created with the initial message, but it is occupied by * only the client until paired with an agent */ -class GuestPool { +class ManualSelection { constructor() { this.config = { previewRoom: true, @@ -43,9 +43,7 @@ class GuestPool { // remove agent from room in case the rooms is being transferred or returned to the Queue LivechatRooms.removeAgentByRoomId(rid); - - const agentIds = allAgents.map((agent) => (department ? agent.agentId : agent._id)); - LivechatInquiry.queueInquiryWithAgents(inquiry._id, agentIds); + LivechatInquiry.queueInquiry(inquiry._id); // Alert only the online agents of the queued request const onlineAgents = Livechat.getOnlineAgents(department); @@ -86,4 +84,4 @@ class GuestPool { } } -RoutingManager.registerMethod('Guest_Pool', GuestPool); +RoutingManager.registerMethod('Manual_Selection', ManualSelection); diff --git a/app/livechat/server/publications/livechatAgents.js b/app/livechat/server/publications/livechatAgents.js index b2048e3ad8e..35855228d25 100644 --- a/app/livechat/server/publications/livechatAgents.js +++ b/app/livechat/server/publications/livechatAgents.js @@ -7,7 +7,7 @@ Meteor.publish('livechat:agents', function() { return this.error(new Meteor.Error('error-not-authorized', 'Not authorized', { publish: 'livechat:agents' })); } - if (!hasPermission(this.userId, 'view-l-room')) { + if (!hasPermission(this.userId, 'manage-livechat-agents')) { return this.error(new Meteor.Error('error-not-authorized', 'Not authorized', { publish: 'livechat:agents' })); } diff --git a/app/livechat/server/publications/livechatManagers.js b/app/livechat/server/publications/livechatManagers.js index 9a064efcb4d..361d65ab3d7 100644 --- a/app/livechat/server/publications/livechatManagers.js +++ b/app/livechat/server/publications/livechatManagers.js @@ -7,7 +7,7 @@ Meteor.publish('livechat:managers', function() { return this.error(new Meteor.Error('error-not-authorized', 'Not authorized', { publish: 'livechat:managers' })); } - if (!hasPermission(this.userId, 'view-livechat-rooms')) { + if (!hasPermission(this.userId, 'manage-livechat-managers')) { return this.error(new Meteor.Error('error-not-authorized', 'Not authorized', { publish: 'livechat:managers' })); } diff --git a/app/livechat/server/publications/livechatRooms.js b/app/livechat/server/publications/livechatRooms.js index 646fdbaeb2a..1009cf649d6 100644 --- a/app/livechat/server/publications/livechatRooms.js +++ b/app/livechat/server/publications/livechatRooms.js @@ -3,13 +3,25 @@ import { Match, check } from 'meteor/check'; import { hasPermission } from '../../../authorization'; import { LivechatDepartment, LivechatRooms } from '../../../models'; +import { canAccessRoom } from '../../../authorization/server/functions/canAccessRoom'; + +const userCanAccessRoom = ({ _id }) => { + if (!_id) { + return; + } + + const room = LivechatRooms.findOneById(_id); + const user = Meteor.user(); + + return canAccessRoom(room, user); +}; Meteor.publish('livechat:rooms', function(filter = {}, offset = 0, limit = 20) { if (!this.userId) { return this.error(new Meteor.Error('error-not-authorized', 'Not authorized', { publish: 'livechat:rooms' })); } - if (!hasPermission(this.userId, 'view-livechat-rooms')) { + if (!hasPermission(this.userId, 'view-livechat-rooms') && !userCanAccessRoom(filter)) { return this.error(new Meteor.Error('error-not-authorized', 'Not authorized', { publish: 'livechat:rooms' })); } @@ -73,7 +85,6 @@ Meteor.publish('livechat:rooms', function(filter = {}, offset = 0, limit = 20) { } const self = this; - const handle = LivechatRooms.findLivechat(query, offset, limit).observeChanges({ added(id, fields) { fields = Object.assign(fields, { lookupDepartment: fields.departmentId ? LivechatDepartment.findOneById(fields.departmentId) : {} }); @@ -81,7 +92,7 @@ Meteor.publish('livechat:rooms', function(filter = {}, offset = 0, limit = 20) { }, changed(id, fields) { fields = Object.assign(fields, { lookupDepartment: fields.departmentId ? LivechatDepartment.findOneById(fields.departmentId) : {} }); - self.added('livechatRoom', id, fields); + self.changed('livechatRoom', id, fields); }, removed(id) { self.removed('livechatRoom', id); diff --git a/app/livechat/server/startup.js b/app/livechat/server/startup.js index 343b11ca9c8..9fdaf13f021 100644 --- a/app/livechat/server/startup.js +++ b/app/livechat/server/startup.js @@ -18,6 +18,15 @@ Meteor.startup(() => { return room && room.t === 'l' && user && hasPermission(user._id, 'view-livechat-rooms'); }); + addRoomAccessValidator(function(room, user) { + if (!room || !user || room.t !== 'l') { + return; + } + const { _id: userId } = user; + const { servedBy: { _id: agentId } = {} } = room; + return userId === agentId; + }); + addRoomAccessValidator(function(room, user, extraData) { if (!room && extraData && extraData.rid) { room = LivechatRooms.findOneById(extraData.rid); diff --git a/app/models/server/models/LivechatRooms.js b/app/models/server/models/LivechatRooms.js index 83a370bdb48..1f622b0fecd 100644 --- a/app/models/server/models/LivechatRooms.js +++ b/app/models/server/models/LivechatRooms.js @@ -430,22 +430,6 @@ export class LivechatRooms extends Base { this.remove(query); } - setNameById(_id, name, fname) { - const query = { - _id, - t: 'l', - }; - - const update = { - $set: { - name, - fname, - }, - }; - - return this.update(query, update); - } - removeById(_id) { const query = { _id, diff --git a/app/models/server/models/Settings.js b/app/models/server/models/Settings.js index 2a45a73fb66..6cca3203f25 100644 --- a/app/models/server/models/Settings.js +++ b/app/models/server/models/Settings.js @@ -166,6 +166,40 @@ export class Settings extends Base { return this.update(query, update); } + addOptionValueById(_id, option = {}) { + const query = { + blocked: { $ne: true }, + _id, + }; + + const { key, i18nLabel } = option; + const update = { + $addToSet: { + values: { + key, + i18nLabel, + }, + }, + }; + + return this.update(query, update); + } + + removeOptionValueByIdAndKey(_id, key) { + const query = { + blocked: { $ne: true }, + _id, + }; + + const update = { + $pull: { + values: { key }, + }, + }; + + return this.update(query, update); + } + // INSERT createWithIdAndValue(_id, value) { const record = { diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 3bc8e6270a0..badfca41d0e 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -422,6 +422,7 @@ "Automatic_Translation": "Automatic Translation", "Author_Site": "Author site", "AutoTranslate": "Auto-Translate", + "Auto_Selection": "Auto Selection", "Auto_Translate": "Auto-Translate", "AutoTranslate_APIKey": "API Key", "AutoTranslate_Change_Language_Description": "Changing the auto-translate language does not translate previous messages.", @@ -1515,7 +1516,6 @@ "Group_favorites": "Group favorites", "Group_mentions_disabled_x_members": "Group mentions `@all` and `@here` have been disabled for rooms with more than __total__ members.", "Group_mentions_only": "Group mentions only", - "Guest_Pool": "Guest Pool", "Hash": "Hash", "Header": "Header", "Header_and_Footer": "Header and Footer", @@ -1911,7 +1911,6 @@ "LDAP_Username_Field_Description": "Which field will be used as *username* for new users. Leave empty to use the username informed on login page.
You can use template tags too, like `#{givenName}.#{sn}`.
Default value is `sAMAccountName`.", "Lead_capture_email_regex": "Lead capture email regex", "Lead_capture_phone_regex": "Lead capture phone regex", - "Least_Amount": "Least Amount", "leave-c": "Leave Channels", "leave-p": "Leave Private Groups", "Leave": "Leave", @@ -2033,6 +2032,7 @@ "Manager_removed": "Manager removed", "Managing_assets": "Managing assets", "Managing_integrations": "Managing integrations", + "Manual_Selection": "Manual Selection", "Manufacturing": "Manufacturing", "MapView_Enabled": "Enable Mapview", "MapView_Enabled_Description": "Enabling mapview will display a location share button on the left of the chat input field.", @@ -3395,4 +3395,4 @@ "Your_question": "Your question", "Your_server_link": "Your server link", "Your_workspace_is_ready": "Your workspace is ready to use šŸŽ‰" -} \ No newline at end of file +} diff --git a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json index e426825e97c..d882a820cfc 100644 --- a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -388,6 +388,7 @@ "auto-translate": "Traduzir automaticamente", "auto-translate_description": "PermissĆ£o para usar a ferramenta de tradução automĆ”tica", "Auto_Load_Images": "Auto Carregar Imagens", + "Auto_Selection": "Auto Seleção", "AutoLinker": "AutoLinker", "AutoLinker_Email": "Aplicar para Email", "AutoLinker_Phone": "Aplicar para Telefone", @@ -1924,6 +1925,7 @@ "Managers": "Gerentes", "Managing_assets": "Gerenciando recursos", "Managing_integrations": "Gerenciando integraƧƵes", + "Manual_Selection": "Seleção Manual", "Manufacturing": "Fabricação", "MapView_Enabled": "Ativar Mapview", "MapView_Enabled_Description": "A ativação do mapa exibirĆ” um botĆ£o de compartilhamento de localização Ć  esquerda do campo de entrada do bate-papo.", @@ -3234,4 +3236,4 @@ "Your_question": "A sua pergunta", "Your_server_link": "O link do seu servidor", "Your_workspace_is_ready": "O seu espaƧo de trabalho estĆ” pronto a usar šŸŽ‰" -} \ No newline at end of file +} diff --git a/server/startup/migrations/index.js b/server/startup/migrations/index.js index e1521ba73b0..5ade91ac489 100644 --- a/server/startup/migrations/index.js +++ b/server/startup/migrations/index.js @@ -152,4 +152,5 @@ import './v151'; import './v152'; import './v153'; import './v154'; +import './v155'; import './xrun'; diff --git a/server/startup/migrations/v155.js b/server/startup/migrations/v155.js new file mode 100644 index 00000000000..248f628d60a --- /dev/null +++ b/server/startup/migrations/v155.js @@ -0,0 +1,34 @@ +import { Migrations } from '../../../app/migrations/server'; +import { Settings } from '../../../app/models/server'; + +Migrations.add({ + version: 155, + up() { + const _id = 'Livechat_Routing_Method'; + const setting = Settings.findOne({ _id }); + if (setting) { + const { value } = setting; + + let newValue; + switch (value) { + case 'Least_Amount': + newValue = 'Auto_Selection'; + break; + case 'Guest_Pool': + newValue = 'Manual_Selection'; + break; + } + + if (!newValue) { + return; + } + + Settings.update({ _id }, { + $set: { + value: newValue, + packageValue: newValue, + }, + }); + } + }, +});