Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 22 additions & 24 deletions app/livechat/client/views/app/livechatAgents.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,21 @@ import s from 'underscore.string';
import _ from 'underscore';

import { modal, call } from '../../../../ui-utils';
import { t, handleError } from '../../../../utils';
import { AgentUsers } from '../../collections/AgentUsers';


import { t, handleError, APIClient } from '../../../../utils/client';
import './livechatAgents.html';

const loadAgents = async (instance, limit = 50) => {
const { users } = await APIClient.v1.get(`livechat/users/agent?count=${ limit }`);
instance.agents.set(users);
instance.ready.set(true);
};

const getUsername = (user) => user.username;
Template.livechatAgents.helpers({
exceptionsAgents() {
const { selectedAgents } = Template.instance();
return AgentUsers.find({}, { fields: { username: 1 } })
.fetch()
.map(getUsername)
.concat(selectedAgents.get().map(getUsername));
return Template.instance().agents.get()
.map(getUsername).concat(selectedAgents.get().map(getUsername));
},
deleteLastAgent() {
const i = Template.instance();
Expand All @@ -33,7 +34,7 @@ Template.livechatAgents.helpers({
return Template.instance().state.get('loading');
},
agents() {
return Template.instance().agents();
return Template.instance().getAgentsWithCriteria();
},
emailAddress() {
if (this.emails && this.emails.length > 0) {
Expand Down Expand Up @@ -82,7 +83,7 @@ Template.livechatAgents.helpers({
const DEBOUNCE_TIME_FOR_SEARCH_AGENTS_IN_MS = 300;

Template.livechatAgents.events({
'click .remove-agent'(e /* , instance*/) {
'click .remove-agent'(e, instance) {
e.preventDefault();

modal.open(
Expand All @@ -97,12 +98,13 @@ Template.livechatAgents.events({
html: false,
},
() => {
Meteor.call('livechat:removeAgent', this.username, function(
Meteor.call('livechat:removeAgent', this.username, async function(
error /* , result*/
) {
if (error) {
return handleError(error);
}
await loadAgents(instance);
modal.open({
title: t('Removed'),
text: t('Agent_removed'),
Expand Down Expand Up @@ -130,6 +132,7 @@ Template.livechatAgents.events({
await Promise.all(
users.map(({ username }) => call('livechat:addAgent', username))
);
await loadAgents(instance);
selectedAgents.set([]);
} finally {
state.set('loading', false);
Expand Down Expand Up @@ -171,6 +174,7 @@ Template.livechatAgents.onCreated(function() {
});
this.ready = new ReactiveVar(true);
this.selectedAgents = new ReactiveVar([]);
this.agents = new ReactiveVar([]);

this.onSelectAgents = ({ item: agent }) => {
this.selectedAgents.set([...this.selectedAgents.curValue, agent]);
Expand All @@ -181,25 +185,19 @@ Template.livechatAgents.onCreated(function() {
};

this.autorun(function() {
const filter = instance.filter.get();
const limit = instance.limit.get();
const subscription = instance.subscribe('livechat:agents', filter, limit);
instance.ready.set(subscription.ready());
loadAgents(instance, limit);
});
this.agents = function() {
this.getAgentsWithCriteria = function() {
let filter;
let query = {};

if (instance.filter && instance.filter.get()) {
filter = s.trim(instance.filter.get());
}

if (filter) {
const filterReg = new RegExp(s.escapeRegExp(filter), 'i');
query = { $or: [{ username: filterReg }, { name: filterReg }, { 'emails.address': filterReg }] };
}

const limit = instance.limit && instance.limit.get();
return AgentUsers.find(query, { limit, sort: { name: 1 } }).fetch();
const regex = new RegExp(s.escapeRegExp(filter), 'i');
return instance.agents.get()
.filter((agent) => agent.name.match(regex)
|| agent.username.match(regex)
|| agent.emails.some((email) => email.address.match(regex)));
};
});
13 changes: 5 additions & 8 deletions app/livechat/client/views/app/livechatDepartmentForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import toastr from 'toastr';

import { t, handleError } from '../../../../utils';
import { hasPermission } from '../../../../authorization';
import { AgentUsers } from '../../collections/AgentUsers';
import { getCustomFormTemplate } from './customTemplates/register';
import './livechatDepartmentForm.html';
import { APIClient } from '../../../../utils/client';
Expand All @@ -22,10 +21,6 @@ Template.livechatDepartmentForm.helpers({
selectedAgents() {
return _.sortBy(Template.instance().selectedAgents.get(), 'username');
},
availableAgents() {
const selected = _.pluck(Template.instance().selectedAgents.get(), 'username');
return AgentUsers.find({ username: { $nin: selected } }, { sort: { username: 1 } });
},
showOnRegistration(value) {
const department = Template.instance().department.get();
return department.showOnRegistration === value || (department.showOnRegistration === undefined && value === true);
Expand Down Expand Up @@ -147,7 +142,7 @@ Template.livechatDepartmentForm.events({
}

input.value = '';
const agent = AgentUsers.findOne({ username });
const agent = Template.instance().agents.get().find((agent) => agent.username === username);
if (!agent) {
return toastr.error(t('The_selected_user_is_not_an_agent'));
}
Expand Down Expand Up @@ -182,11 +177,13 @@ Template.livechatDepartmentForm.events({
},
});

Template.livechatDepartmentForm.onCreated(function() {
Template.livechatDepartmentForm.onCreated(async function() {
this.department = new ReactiveVar({ enabled: true });
this.selectedAgents = new ReactiveVar([]);
this.agents = new ReactiveVar([]);

this.subscribe('livechat:agents');
const { users } = await APIClient.v1.get('livechat/users/agent');
this.agents.set(users);

this.autorun(async () => {
const id = FlowRouter.getParam('_id');
Expand Down
10 changes: 6 additions & 4 deletions app/livechat/client/views/app/livechatQueue.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { settings } from '../../../../settings';
import { hasPermission } from '../../../../authorization';
import { Users } from '../../../../models';
import { LivechatQueueUser } from '../../collections/LivechatQueueUser';
import { AgentUsers } from '../../collections/AgentUsers';
import './livechatQueue.html';
import { APIClient } from '../../../../utils/client';

Expand All @@ -31,9 +30,9 @@ Template.livechatQueue.helpers({
}).forEach((user) => {
const options = { fields: { _id: 1 } };
const userFilter = { _id: user.agentId, status: { $ne: 'offline' } };
const agentFilter = { _id: user.agentId, statusLivechat: 'available' };
const agent = Template.instance().agents.get().find((agent) => agent._id === user.agentId && agent.statusLivechat === 'available');

if (showOffline[this._id] || (Meteor.users.findOne(userFilter, options) && AgentUsers.findOne(agentFilter, options))) {
if (showOffline[this._id] || (Meteor.users.findOne(userFilter, options) && agent)) {
users.push(user);
}
});
Expand All @@ -59,10 +58,13 @@ Template.livechatQueue.events({

Template.livechatQueue.onCreated(async function() {
this.showOffline = new ReactiveVar({});
this.agents = new ReactiveVar([]);
this.departments = new ReactiveVar([]);

this.subscribe('livechat:queue');
this.subscribe('livechat:agents');
const { users } = await APIClient.v1.get('livechat/users/agent');
const { departments } = await APIClient.v1.get('livechat/department?sort={"name": 1}');

this.agents.set(users);
this.departments.set(departments);
});
15 changes: 6 additions & 9 deletions app/livechat/client/views/app/tabbar/visitorForward.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import toastr from 'toastr';

import { ChatRoom } from '../../../../../models';
import { t } from '../../../../../utils';
import { AgentUsers } from '../../../collections/AgentUsers';
import './visitorForward.html';
import { APIClient } from '../../../../../utils/client';

Expand All @@ -21,13 +20,8 @@ Template.visitorForward.helpers({
return Template.instance().departments.get().filter((department) => department.enabled === true);
},
agents() {
const query = {
_id: { $ne: Meteor.userId() },
status: { $ne: 'offline' },
statusLivechat: 'available',
};

return AgentUsers.find(query, { sort: { name: 1, username: 1 } });
return Template.instance().agents.get()
.filter((agent) => agent._id !== Meteor.userId() && agent.status !== 'offline' && agent.statusLivechat === 'available');
},
agentName() {
return this.name || this.username;
Expand All @@ -37,6 +31,7 @@ Template.visitorForward.helpers({
Template.visitorForward.onCreated(async function() {
this.visitor = new ReactiveVar();
this.room = new ReactiveVar();
this.agents = new ReactiveVar([]);
this.departments = new ReactiveVar([]);

this.autorun(() => {
Expand All @@ -46,8 +41,10 @@ Template.visitorForward.onCreated(async function() {
this.autorun(() => {
this.room.set(ChatRoom.findOne({ _id: Template.currentData().roomId }));
});
this.subscribe('livechat:agents');

const { users } = await APIClient.v1.get('livechat/users/agent?sort={"name": 1, "username": 1}');
const { departments } = await APIClient.v1.get('livechat/department');
this.agents.set(users);
this.departments.set(departments);
});

Expand Down
52 changes: 27 additions & 25 deletions app/livechat/imports/server/rest/users.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,41 @@
import { check } from 'meteor/check';
import _ from 'underscore';

import { hasPermission, getUsersInRole } from '../../../../authorization';
import { hasPermission } from '../../../../authorization';
import { API } from '../../../../api';
import { Users } from '../../../../models';
import { Livechat } from '../../../server/lib/Livechat';
import { findAgents, findManagers } from '../../../server/api/lib/users';

API.v1.addRoute('livechat/users/:type', { authRequired: true }, {
get() {
if (!hasPermission(this.userId, 'view-livechat-manager')) {
return API.v1.unauthorized();
check(this.urlParams, {
type: String,
});
const { offset, count } = this.getPaginationItems();
const { sort } = this.parseJsonQuery();

if (this.urlParams.type === 'agent') {
return API.v1.success(Promise.await(findAgents({
userId: this.userId,
pagination: {
offset,
count,
sort,
},
})));
}

try {
check(this.urlParams, {
type: String,
});

let role;
if (this.urlParams.type === 'agent') {
role = 'livechat-agent';
} else if (this.urlParams.type === 'manager') {
role = 'livechat-manager';
} else {
throw new Error('Invalid type');
}

const users = getUsersInRole(role);

return API.v1.success({
users: users.fetch().map((user) => _.pick(user, '_id', 'username', 'name', 'status', 'statusLivechat')),
});
} catch (e) {
return API.v1.failure(e.error);
if (this.urlParams.type === 'manager') {
return API.v1.success(Promise.await(findManagers({
userId: this.userId,
pagination: {
offset,
count,
sort,
},
})));
}
throw new Error('Invalid type');
},
post() {
if (!hasPermission(this.userId, 'view-livechat-manager')) {
Expand Down
55 changes: 55 additions & 0 deletions app/livechat/server/api/lib/users.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission';
import { Users } from '../../../../models/server/raw';

async function findUsers({ userId, role, pagination: { offset, count, sort } }) {
if (!await hasPermissionAsync(userId, 'view-livechat-manager') || !await hasPermissionAsync(userId, 'manage-livechat-agents')) {
throw new Error('error-not-authorized');
}

const cursor = await Users.findUsersInRoles(role, undefined, {
sort: sort || { name: 1 },
skip: offset,
limit: count,
fields: {
username: 1,
name: 1,
status: 1,
statusLivechat: 1,
emails: 1,
},
});

const total = await cursor.count();

const users = await cursor.toArray();

return {
users,
count: users.length,
offset,
total,
};
}
export async function findAgents({ userId, pagination: { offset, count, sort } }) {
return findUsers({
role: 'livechat-agent',
userId,
pagination: {
offset,
count,
sort,
},
});
}

export async function findManagers({ userId, pagination: { offset, count, sort } }) {
return findUsers({
role: 'livechat-manager',
userId,
pagination: {
offset,
count,
sort,
},
});
}
4 changes: 2 additions & 2 deletions app/livechat/server/publications/livechatDepartments.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { LivechatDepartment } from '../../../models';

Meteor.publish('livechat:departments', function(_id, limit = 50) {
if (!this.userId) {
return this.error(new Meteor.Error('error-not-authorized', 'Not authorized', { publish: 'livechat:agents' }));
return this.error(new Meteor.Error('error-not-authorized', 'Not authorized', { publish: 'livechat:departments' }));
}

if (!hasPermission(this.userId, 'view-l-room')) {
return this.error(new Meteor.Error('error-not-authorized', 'Not authorized', { publish: 'livechat:agents' }));
return this.error(new Meteor.Error('error-not-authorized', 'Not authorized', { publish: 'livechat:departments' }));
}

if (_id) {
Expand Down
2 changes: 1 addition & 1 deletion app/livechat/server/publications/livechatOfficeHours.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { LivechatOfficeHour } from '../../../models';

Meteor.publish('livechat:officeHour', function() {
if (!hasPermission(this.userId, 'view-l-room')) {
return this.error(new Meteor.Error('error-not-authorized', 'Not authorized', { publish: 'livechat:agents' }));
return this.error(new Meteor.Error('error-not-authorized', 'Not authorized', { publish: 'livechat:officeHour' }));
}

return LivechatOfficeHour.find();
Expand Down
8 changes: 4 additions & 4 deletions app/models/server/raw/BaseRaw.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ export class BaseRaw {
return this.col.findOne(...args);
}

find(...args) {
return this.col.find(...args);
}

findUsersInRoles() {
throw new Error('overwrite-function', 'You must overwrite this function in the extended classes');
}

find(...args) {
return this.col.find(...args);
}
}
Loading