Merge branch 'develop' into tests

pull/5072/head
Gabriel Engel 9 years ago
commit d4d68bf496
  1. 67
      packages/rocketchat-api/server/routes.coffee
  2. 1
      packages/rocketchat-authorization/server/startup.coffee
  3. 6
      packages/rocketchat-i18n/i18n/en.i18n.json
  4. 3
      packages/rocketchat-i18n/i18n/pt.i18n.json
  5. 2
      packages/rocketchat-lib/package.js
  6. 11
      packages/rocketchat-lib/server/functions/settings.coffee
  7. 34
      packages/rocketchat-lib/server/methods/cleanChannelHistory.js
  8. 81
      packages/rocketchat-lib/server/methods/getChannelHistory.js
  9. 21
      packages/rocketchat-lib/server/models/Messages.coffee
  10. 24
      packages/rocketchat-livechat/app/client/lib/_livechat.js
  11. 19
      packages/rocketchat-livechat/app/client/lib/chatMessages.coffee
  12. 4
      packages/rocketchat-livechat/app/client/lib/commands.js
  13. 8
      packages/rocketchat-livechat/app/client/lib/hooks.js
  14. 45
      packages/rocketchat-livechat/app/client/stylesheets/loading.less
  15. 8
      packages/rocketchat-livechat/app/client/views/loading.html
  16. 24
      packages/rocketchat-livechat/app/client/views/messages.html
  17. 3
      packages/rocketchat-livechat/app/client/views/messages.js
  18. 4
      packages/rocketchat-livechat/app/client/views/register.html
  19. 11
      packages/rocketchat-livechat/app/client/views/register.js
  20. 12
      packages/rocketchat-livechat/assets/rocket-livechat.js
  21. 7
      packages/rocketchat-livechat/client/views/app/livechatDepartmentForm.html
  22. 8
      packages/rocketchat-livechat/client/views/app/livechatDepartmentForm.js
  23. 10
      packages/rocketchat-livechat/client/views/app/livechatDepartments.html
  24. 8
      packages/rocketchat-livechat/config.js
  25. 18
      packages/rocketchat-livechat/server/lib/Livechat.js
  26. 2
      packages/rocketchat-livechat/server/lib/QueueMethods.js
  27. 5
      packages/rocketchat-livechat/server/methods/takeInquiry.js
  28. 12
      packages/rocketchat-livechat/server/models/LivechatDepartment.js
  29. 4
      packages/rocketchat-livechat/server/models/LivechatDepartmentAgents.js
  30. 12
      packages/rocketchat-sandstorm/client/setPath.js
  31. 4
      packages/rocketchat-sandstorm/package.js
  32. 17
      packages/rocketchat-theme/assets/stylesheets/base.less
  33. 84
      packages/rocketchat-theme/assets/stylesheets/utils/_colors.import.less
  34. 11
      packages/rocketchat-theme/server/variables.coffee
  35. 2
      packages/rocketchat-ui-admin/admin/admin.coffee
  36. 14
      packages/rocketchat-ui-flextab/flex-tab/tabs/uploadedFilesList.coffee
  37. 4
      packages/rocketchat-ui-flextab/flex-tab/tabs/uploadedFilesList.html
  38. 3
      packages/rocketchat-ui-sidenav/side-nav/accountBox.coffee
  39. 2
      packages/rocketchat-ui-sidenav/side-nav/accountBox.html
  40. 12
      server/startup/migrations/v067.js

@ -105,6 +105,73 @@ RocketChat.API.v1.addRoute 'channels.create', authRequired: true,
return RocketChat.API.v1.success
channel: RocketChat.models.Rooms.findOneById(id.rid)
RocketChat.API.v1.addRoute 'channels.history', authRequired: true,
get: ->
if not @queryParams.roomId?
return RocketChat.API.v1.failure 'Query parameter "roomId" is required.'
rid = @queryParams.roomId
latestDate = new Date
if @queryParams.latest?
latestDate = new Date(@queryParams.latest)
oldestDate = undefined
if @queryParams.oldest?
oldestDate = new Date(@queryParams.oldest)
inclusive = false
if @queryParams.inclusive?
inclusive = @queryParams.inclusive
count = 20
if @queryParams.count?
count = parseInt @queryParams.count
unreads = false
if @queryParams.unreads?
unreads = @queryParams.unreads
result = {}
try
Meteor.runAsUser this.userId, =>
result = Meteor.call 'getChannelHistory', { rid, latest: latestDate, oldest: oldestDate, inclusive, count, unreads }
catch e
return RocketChat.API.v1.failure e.name + ': ' + e.message
return RocketChat.API.v1.success
result: result
RocketChat.API.v1.addRoute 'channels.cleanHistory', authRequired: true,
post: ->
if not @bodyParams.roomId?
return RocketChat.API.v1.failure 'Body parameter "roomId" is required.'
roomId = @bodyParams.roomId
if not @bodyParams.latest?
return RocketChat.API.v1.failure 'Body parameter "latest" is required.'
if not @bodyParams.oldest?
return RocketChat.API.v1.failure 'Body parameter "oldest" is required.'
latest = new Date(@bodyParams.latest)
oldest = new Date(@bodyParams.oldest)
inclusive = false
if @bodyParams.inclusive?
inclusive = @bodyParams.inclusive
try
Meteor.runAsUser this.userId, =>
Meteor.call 'cleanChannelHistory', { roomId, latest, oldest, inclusive }
catch e
return RocketChat.API.v1.failure e.name + ': ' + e.message
return RocketChat.API.v1.success
success: true
# List Private Groups a user has access to
RocketChat.API.v1.addRoute 'groups.list', authRequired: true,
get: ->

@ -17,6 +17,7 @@ Meteor.startup ->
{ _id: 'create-d', roles : ['admin', 'user'] }
{ _id: 'create-p', roles : ['admin', 'user'] }
{ _id: 'create-user', roles : ['admin'] }
{ _id: 'clean-channel-history', roles : ['admin'] } # special permission to bulk delete a channel's mesages
{ _id: 'delete-c', roles : ['admin'] }
{ _id: 'delete-d', roles : ['admin'] }
{ _id: 'delete-message', roles : ['admin', 'owner', 'moderator'] }

@ -424,6 +424,7 @@
"error-invalid-channel-start-with-chars": "Invalid channel. Start with @ or #",
"error-invalid-custom-field": "Invalid custom field",
"error-invalid-custom-field-name": "Invalid custom field name. Use only letters, numbers, hyphens and underscores.",
"error-invalid-date": "Invalid date provided.",
"error-invalid-description": "Invalid description",
"error-invalid-domain": "Invalid domain",
"error-invalid-email": "Invalid email __email__",
@ -764,6 +765,7 @@
"Livechat_managers": "Livechat managers",
"Livechat_offline": "Livechat offline",
"Livechat_online": "Livechat online",
"Livechat_open_inquiery_show_connecting": "Show connecting message instead of input when guest is not yet connected to an agent",
"Livechat_Queue": "Livechat Queue",
"Livechat_room_count": "Livechat room count",
"Livechat_Routing_Method": "Livechat Routing Method",
@ -1167,6 +1169,7 @@
"Show_all": "Show all",
"Show_more": "Show more",
"show_offline_users": "show offline users",
"Show_on_registration_page": "Show on registration page",
"Show_only_online": "Show only online",
"Show_preregistration_form": "Show pre-registration form",
"Show_queue_list_to_all_agents": "Show queue list to all agents",
@ -1278,6 +1281,7 @@
"theme-color-transparent-dark": "Transparent Dark",
"theme-color-transparent-light": "Transparent Light",
"theme-color-transparent-lighter": "Transparent Lighter",
"theme-color-transparent-lightest": "Transparent Lightest",
"theme-color-content-background-color": "Content Background Color",
"theme-color-primary-background-color": "Primary Background Color",
"theme-color-primary-font-color": "Primary Font Color",
@ -1488,4 +1492,4 @@
"your_message_optional": "your message (optional)",
"Your_password_is_wrong": "Your password is wrong!",
"Your_push_was_sent_to_s_devices": "Your push was sent to %s devices"
}
}

@ -1035,6 +1035,7 @@
"Show_all": "Mostrar tudo",
"Show_more": "Mostrar mais",
"show_offline_users": "mostrar usuários offline",
"Show_on_registration_page": "Mostrar no formulário de registro",
"Show_only_online": "Mostrar apenas online",
"Show_preregistration_form": "Mostrar formulário de pré-registro",
"Show_queue_list_to_all_agents": "Mostrar link da roleta à todos agentes",
@ -1306,4 +1307,4 @@
"your_message_optional": "sua mensagem (opcional)",
"Your_password_is_wrong": "Sua senha está errada!",
"Your_push_was_sent_to_s_devices": "Sua natificação foi enviada para %s dispositivos"
}
}

@ -137,6 +137,8 @@ Package.onUse(function(api) {
api.addFiles('server/methods/updateMessage.coffee', 'server');
api.addFiles('server/methods/filterBadWords.js', ['server']);
api.addFiles('server/methods/filterATAllTag.js', 'server');
api.addFiles('server/methods/getChannelHistory.js', 'server');
api.addFiles('server/methods/cleanChannelHistory.js', 'server');
// SERVER STARTUP
api.addFiles('server/startup/settingsOnLoadCdnPrefix.coffee', 'server');

@ -92,9 +92,16 @@ RocketChat.settings.add = (_id, value, options = {}) ->
updateOperations.$unset = { section: 1 }
query.section = { $exists: false }
if not RocketChat.models.Settings.findOne(query)?
existantSetting = RocketChat.models.Settings.findOne(query)
if existantSetting?
if not existantSetting.editor? and updateOperations.$setOnInsert.editor?
updateOperations.$set.editor = updateOperations.$setOnInsert.editor
delete updateOperations.$setOnInsert.editor
else
updateOperations.$set.ts = new Date
return RocketChat.models.Settings.upsert { _id: _id }, updateOperations
return RocketChat.models.Settings.upsert { _id: _id }, updateOperations

@ -0,0 +1,34 @@
Meteor.methods({
cleanChannelHistory({roomId, latest, oldest, inclusive}) {
check(roomId, String);
check(latest, Date);
check(oldest, Date);
check(inclusive, Boolean);
if (!Meteor.userId()) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'cleanChannelHistory' });
}
if (!RocketChat.authz.hasPermission(Meteor.userId(), 'clean-channel-history')) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'cleanChannelHistory' });
}
if (inclusive) {
RocketChat.models.Messages.remove({
rid: roomId,
ts: {
$gte: oldest,
$lte: latest
}
});
} else {
RocketChat.models.Messages.remove({
rid: roomId,
ts: {
$gt: oldest,
$lt: latest
}
});
}
}
});

@ -0,0 +1,81 @@
Meteor.methods({
getChannelHistory({rid, latest, oldest, inclusive, count = 20, unreads}) {
check(rid, String);
if (!Meteor.userId()) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getChannelHistory' });
}
const fromUserId = Meteor.userId();
const room = Meteor.call('canAccessRoom', rid, fromUserId);
if (!room) {
return false;
}
//Make sure they can access the room
if (room.t === 'c' && !RocketChat.authz.hasPermission(fromUserId, 'preview-c-room') && room.usernames.indexOf(room.username) === -1) {
return false;
}
//Ensure latest is always defined.
if (_.isUndefined(latest)) {
latest = new Date();
}
//Verify oldest is a date if it exists
if (!_.isUndefined(oldest) && !_.isDate(oldest)) {
throw new Meteor.Error('error-invalid-date', 'Invalid date', { method: 'getChannelHistory' });
}
const options = {
sort: {
ts: -1
},
limit: count
};
if (!RocketChat.settings.get('Message_ShowEditedStatus')) {
options.fields = { 'editedAt': 0 };
}
let records = [];
if (_.isUndefined(oldest) && inclusive) {
records = RocketChat.models.Messages.findVisibleByRoomIdBeforeTimestampInclusive(rid, latest, options).fetch();
} else if (_.isUndefined(oldest) && !inclusive) {
records = RocketChat.models.Messages.findVisibleByRoomIdBeforeTimestamp(rid, latest, options).fetch();
} else if (!_.isUndefined(oldest) && inclusive) {
records = RocketChat.models.Messages.findVisibleByRoomIdBetweenTimestampsInclusive(rid, oldest, latest, options).fetch();
} else {
records = RocketChat.models.Messages.findVisibleByRoomIdBetweenTimestamps(rid, oldest, latest, options).fetch();
}
const messages = _.map(records, (message) => {
message.starred = _.findWhere(message.starred, { _id: fromUserId });
return message;
});
if (unreads) {
let unreadNotLoaded = 0;
let firstUnread = undefined;
if (!_.isUndefined(oldest)) {
const firstMsg = messages[messages.length - 1];
if (!_.isUndefined(firstMsg) && firstMsg.ts > oldest) {
const unreadMessages = RocketChat.models.Messages.findVisibleByRoomIdBetweenTimestamps(rid, oldest, firstMsg.ts, { limit: 1, sort: { ts: 1 } });
firstUnread = unreadMessages.fetch()[0];
unreadNotLoaded = unreadMessages.count();
}
}
return {
messages,
firstUnread,
unreadNotLoaded
};
}
return {
messages
};
}
});

@ -89,6 +89,16 @@ RocketChat.models.Messages = new class extends RocketChat.models._Base
return @find query, options
findVisibleByRoomIdBeforeTimestampInclusive: (roomId, timestamp, options) ->
query =
_hidden:
$ne: true
rid: roomId
ts:
$lte: timestamp
return @find query, options
findVisibleByRoomIdBetweenTimestamps: (roomId, afterTimestamp, beforeTimestamp, options) ->
query =
_hidden:
@ -100,6 +110,17 @@ RocketChat.models.Messages = new class extends RocketChat.models._Base
return @find query, options
findVisibleByRoomIdBetweenTimestampsInclusive: (roomId, afterTimestamp, beforeTimestamp, options) ->
query =
_hidden:
$ne: true
rid: roomId
ts:
$gte: afterTimestamp
$lte: beforeTimestamp
return @find query, options
findVisibleByRoomIdBeforeTimestampNotContainingTypes: (roomId, timestamp, types, options) ->
query =
_hidden:

@ -18,15 +18,17 @@ this.Livechat = new (class Livechat {
this._offlineSuccessMessage = new ReactiveVar(TAPi18n.__('Thanks_We_ll_get_back_to_you_soon'));
this._videoCall = new ReactiveVar(false);
this._transcriptMessage = new ReactiveVar('');
this._connecting = new ReactiveVar(false);
this._room = new ReactiveVar(null);
Tracker.autorun((c) => {
this._department = new ReactiveVar(null);
Tracker.autorun(() => {
if (this._room.get() && Meteor.userId()) {
RoomHistoryManager.getMoreIfIsEmpty(this._room.get());
visitor.subscribeToRoom(this._room.get());
visitor.setRoom(this._room.get());
c.stop();
}
});
}
@ -70,6 +72,13 @@ this.Livechat = new (class Livechat {
get transcriptMessage() {
return this._transcriptMessage.get();
}
get department() {
return this._department.get();
}
get connecting() {
return this._connecting.get();
}
set online(value) {
this._online.set(value);
@ -118,8 +127,17 @@ this.Livechat = new (class Livechat {
set transcriptMessage(value) {
this._transcriptMessage.set(value);
}
set connecting(value) {
this._connecting.set(value);
}
set room(roomId) {
this._room.set(roomId);
}
set department(departmentId) {
const dept = Department.findOne({ _id: departmentId }) || Department.findOne({ name: departmentId });
if (dept) {
this._department.set(dept._id);
}
}
})();

@ -76,7 +76,12 @@ class @ChatMessages
rid ?= visitor.getRoom(true)
sendMessage = (callback) ->
msgObject = { _id: Random.id(), rid: rid, msg: msg, token: visitor.getToken() }
msgObject = {
_id: Random.id(),
rid: rid,
msg: msg,
token: visitor.getToken()
}
MsgTyping.stop(rid)
Meteor.call 'sendMessageLivechat', msgObject, (error, result) ->
@ -84,12 +89,20 @@ class @ChatMessages
ChatMessage.update msgObject._id, { $set: { error: true } }
showError error.reason
if result.rid? and not visitor.isSubscribed(result.rid)
if result?.rid? and not visitor.isSubscribed(result.rid)
Livechat.connecting = result.showConnecting
ChatMessage.update result._id, _.omit(result, '_id')
Livechat.room = result.rid
if not Meteor.userId()
Meteor.call 'livechat:registerGuest', { token: visitor.getToken() }, (error, result) ->
guest = {
token: visitor.getToken()
}
if Livechat.department
guest.department = Livechat.department
Meteor.call 'livechat:registerGuest', guest, (error, result) ->
if error?
return showError error.reason

@ -60,5 +60,9 @@ this.Commands = {
showConfirmButton: false
});
}
},
connected: function() {
Livechat.connecting = false;
}
};

@ -19,6 +19,14 @@ var api = {
if (theme.fontColor) {
Livechat.customFontColor = theme.fontColor;
}
},
setDepartment: function(department) {
Livechat.department = department;
},
clearDepartment: function() {
Livechat.department = null;
}
};

@ -0,0 +1,45 @@
@import "_variables.less";
.loading {
color: @secondary-font-color;
font-size: 1.3rem;
margin-left: 32px;
margin-top: 12px;
margin-bottom: 5px;
}
.loading > div {
width: 3px;
height: 3px;
background-color: @secondary-font-color;
border-radius: 100%;
display: inline-block;
-webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
}
.loading .bounce1 {
-webkit-animation-delay: -0.32s;
animation-delay: -0.32s;
}
.loading .bounce2 {
-webkit-animation-delay: -0.16s;
animation-delay: -0.16s;
}
@-webkit-keyframes sk-bouncedelay {
0%, 80%, 100% { -webkit-transform: scale(0) }
40% { -webkit-transform: scale(1.0) }
}
@keyframes sk-bouncedelay {
0%, 80%, 100% {
-webkit-transform: scale(0);
transform: scale(0);
} 40% {
-webkit-transform: scale(1.0);
transform: scale(1.0);
}
}

@ -0,0 +1,8 @@
<template name="loading">
<div class="loading">
{{_ "Connecting to an Agent"}}
<div class="bounce1"></div>
<div class="bounce2"></div>
<div class="bounce3"></div>
</div>
</template>

@ -16,17 +16,21 @@
</div>
</div>
<div class="footer">
<div class="message-bar">
<div class="input-wrapper">
<textarea class="input-message" placeholder="{{_ "Type_your_message"}}"></textarea>
{{#if showConnecting}}
{{> loading}}
{{else}}
<div class="message-bar">
<div class="input-wrapper">
<textarea class="input-message" placeholder="{{_ "Type_your_message"}}"></textarea>
</div>
<div class="buttons">
<svg class="send-button" aria-label="{{_ "Send"}}" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1764 11q33 24 27 64l-256 1536q-5 29-32 45-14 8-31 8-11 0-24-5l-453-185-242 295q-18 23-49 23-13 0-22-4-19-7-30.5-23.5t-11.5-36.5v-349l864-1059-1069 925-395-162q-37-14-40-55-2-40 32-59l1664-960q15-9 32-9 20 0 36 11z"/></svg>
{{#if videoCallEnabled}}
<svg class="video-button" aria-label="{{_ "Video"}}" viewBox="0 0 459 459" xmlns="http://www.w3.org/2000/svg"><path d="M357,191.25V102c0-15.3-10.2-25.5-25.5-25.5h-306C10.2,76.5,0,86.7,0,102v255c0,15.3,10.2,25.5,25.5,25.5h306 c15.3,0,25.5-10.2,25.5-25.5v-89.25l102,102V89.25L357,191.25z"/></svg>
{{/if}}
</div>
</div>
<div class="buttons">
<svg class="send-button" aria-label="{{_ "Send"}}" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1764 11q33 24 27 64l-256 1536q-5 29-32 45-14 8-31 8-11 0-24-5l-453-185-242 295q-18 23-49 23-13 0-22-4-19-7-30.5-23.5t-11.5-36.5v-349l864-1059-1069 925-395-162q-37-14-40-55-2-40 32-59l1664-960q15-9 32-9 20 0 36 11z"/></svg>
{{#if videoCallEnabled}}
<svg class="video-button" aria-label="{{_ "Video"}}" viewBox="0 0 459 459" xmlns="http://www.w3.org/2000/svg"><path d="M357,191.25V102c0-15.3-10.2-25.5-25.5-25.5h-306C10.2,76.5,0,86.7,0,102v255c0,15.3,10.2,25.5,25.5,25.5h306 c15.3,0,25.5-10.2,25.5-25.5v-89.25l102,102V89.25L357,191.25z"/></svg>
{{/if}}
</div>
</div>
{{/if}}
{{> options show=showOptions}}
<button class="toggle-options">{{optionsLink}}</button>

@ -29,6 +29,9 @@ Template.messages.helpers({
},
videoCallEnabled() {
return Livechat.videoCall;
},
showConnecting() {
return Livechat.connecting;
}
});

@ -11,11 +11,11 @@
<input type="text" name="name" id="guestName" placeholder="{{_ "Name"}}">
<input type="email" name="email" id="guestEmail" placeholder="{{_ "Email"}}">
{{#if hasDepartments}}
{{#if showDepartments}}
<select name="department">
<option value="">{{_ "Select_a_department"}}</option>
{{#each departments}}
<option value="{{_id}}">{{name}}</option>
<option value="{{_id}}" selected="{{selectedDepartment}}">{{name}}</option>
{{/each}}
</select>
{{/if}}

@ -7,14 +7,17 @@ Template.register.helpers({
welcomeMessage() {
return '';
},
hasDepartments() {
return Department.find().count() > 1;
showDepartments() {
return Department.find({ showOnRegistration: true }).count() > 1;
},
departments() {
return Department.find();
return Department.find({ showOnRegistration: true });
},
videoCallEnabled() {
return Livechat.videoCall;
},
selectedDepartment() {
return this._id === Livechat.department;
}
});
@ -47,7 +50,7 @@ Template.register.events({
token: visitor.getToken(),
name: $name.val(),
email: $email.val(),
department: departmentId
department: Livechat.deparment || departmentId
};
Meteor.call('livechat:registerGuest', guest, function(error, result) {
if (error != null) {

@ -75,6 +75,14 @@
callHook('setTheme', theme);
};
var setDepartment = function(department) {
callHook('setDepartment', department);
};
var clearDepartment = function() {
callHook('clearDepartment');
};
var currentPage = {
href: null,
title: null
@ -168,7 +176,9 @@
w.RocketChat.livechat = {
pageVisited: pageVisited,
setCustomField: setCustomField,
setTheme: setTheme
setTheme: setTheme,
setDepartment: setDepartment,
clearDepartment: clearDepartment
};
// proccess queue

@ -22,6 +22,13 @@
<textarea name="description" rows="6">{{department.description}}</textarea>
</div>
</div>
<div class="input-line">
<label>{{_ "Show_on_registration_page"}}</label>
<div>
<label><input type="radio" name="showOnRegistration" value="1" checked="{{showOnRegistration true}}" /> {{_ "Yes"}}</label>
<label><input type="radio" name="showOnRegistration" value="0" checked="{{showOnRegistration false}}" /> {{_ "No"}}</label>
</div>
</div>
<hr />
<h2>{{_ "Agents"}}</h2>

@ -12,6 +12,10 @@ Template.livechatDepartmentForm.helpers({
availableAgents() {
var selected = _.pluck(Template.instance().selectedAgents.get(), 'username');
return AgentUsers.find({ username: { $nin: selected }}, { sort: { username: 1 } });
},
showOnRegistration(value) {
let department = Template.instance().department.get();
return department.showOnRegistration === value || (department.showOnRegistration === undefined && value === true);
}
});
@ -24,6 +28,7 @@ Template.livechatDepartmentForm.events({
var enabled = instance.$('input[name=enabled]:checked').val();
var name = instance.$('input[name=name]').val();
var description = instance.$('textarea[name=description]').val();
var showOnRegistration = instance.$('input[name=showOnRegistration]:checked').val();
if (enabled !== '1' && enabled !== '0') {
return toastr.error(t('Please_select_enabled_yes_or_no'));
@ -39,7 +44,8 @@ Template.livechatDepartmentForm.events({
var departmentData = {
enabled: enabled === '1' ? true : false,
name: name.trim(),
description: description.trim()
description: description.trim(),
showOnRegistration: showOnRegistration === '1' ? true : false
};
var departmentAgents = [];

@ -3,10 +3,11 @@
<table>
<thead>
<tr>
<th width="25%">{{_ "Name"}}</th>
<th width="25%">{{_ "Description"}}</th>
<th width="25%">{{_ "Num_Agents"}}</th>
<th width="25%">{{_ "Enabled"}}</th>
<th width="20%">{{_ "Name"}}</th>
<th width="30%">{{_ "Description"}}</th>
<th width="10%">{{_ "Num_Agents"}}</th>
<th width="20%">{{_ "Enabled"}}</th>
<th width="20%">{{_ "Show_on_registration"}}</th>
<th>{{_ "Delete"}}</th>
</tr>
</thead>
@ -17,6 +18,7 @@
<td>{{description}}</td>
<td>{{numAgents}}</td>
<td>{{#if enabled}}{{_ "Yes"}}{{else}}{{_ "No"}}{{/if}}</td>
<td>{{#if showOnRegistration}}{{_ "Yes"}}{{else}}{{_ "No"}}{{/if}}</td>
<td><a href="#remove" class="remove-department"><i class="icon-trash"></i></a></td>
</tr>
{{/each}}

@ -211,4 +211,12 @@ Meteor.startup(function() {
enableQuery: { _id: 'Livechat_enable_transcript', value: true }
});
RocketChat.settings.add('Livechat_open_inquiery_show_connecting', false, {
type: 'boolean',
group: 'Livechat',
public: true,
i18nLabel: 'Livechat_open_inquiery_show_connecting',
enableQuery: { _id: 'Livechat_Routing_Method', value: 'Guest_Pool' }
});
});

@ -68,7 +68,9 @@ RocketChat.Livechat = {
if (guest.name) {
message.alias = guest.name;
}
return _.extend(RocketChat.sendMessage(guest, message, room), { newRoom: newRoom });
// return messages;
return _.extend(RocketChat.sendMessage(guest, message, room), { newRoom: newRoom, showConnecting: this.showConnecting() });
},
registerGuest({ token, name, email, department, phone, loginToken, username } = {}) {
check(token, String);
@ -200,7 +202,6 @@ RocketChat.Livechat = {
RocketChat.sendMessage(user, message, room);
RocketChat.models.Subscriptions.hideByRoomIdAndUserId(room._id, user._id);
RocketChat.models.Messages.createCommandWithRoomIdAndUser('promptTranscript', room._id, user);
Meteor.defer(() => {
@ -465,7 +466,8 @@ RocketChat.Livechat = {
check(departmentData, {
enabled: Boolean,
name: String,
description: Match.Optional(String)
description: Match.Optional(String),
showOnRegistration: Boolean
});
check(departmentAgents, [
@ -482,7 +484,7 @@ RocketChat.Livechat = {
}
}
return RocketChat.models.LivechatDepartment.createOrUpdateDepartment(_id, departmentData.enabled, departmentData.name, departmentData.description, departmentAgents);
return RocketChat.models.LivechatDepartment.createOrUpdateDepartment(_id, departmentData, departmentAgents);
},
removeDepartment(_id) {
@ -495,6 +497,14 @@ RocketChat.Livechat = {
}
return RocketChat.models.LivechatDepartment.removeById(_id);
},
showConnecting() {
if (RocketChat.settings.get('Livechat_Routing_Method') === 'Guest_Pool') {
return RocketChat.settings.get('Livechat_open_inquiery_show_connecting');
} else {
return false;
}
}
};

@ -120,4 +120,4 @@ RocketChat.QueueMethods = {
return room;
}
};
};

@ -51,6 +51,11 @@ Meteor.methods({
// mark inquiry as taken
RocketChat.models.LivechatInquiry.takeInquiry(inquiry._id);
// remove sending message from guest widget
// dont check if setting is true, because if settingwas switched off inbetween guest entered pool,
// and inquiry being taken, message would not be switched off.
RocketChat.models.Messages.createCommandWithRoomIdAndUser('connected', room._id, user);
// return room corresponding to inquiry (for redirecting agent to the room route)
return room;
}

@ -4,6 +4,11 @@
class LivechatDepartment extends RocketChat.models._Base {
constructor() {
super('livechat_department');
this.tryEnsureIndex({
numAgents: 1,
enabled: 1
});
}
// FIND
@ -19,18 +24,17 @@ class LivechatDepartment extends RocketChat.models._Base {
return this.find(query, options);
}
createOrUpdateDepartment(_id, enabled, name, description, agents, extraData) {
createOrUpdateDepartment(_id, { enabled, name, description, showOnRegistration }, agents) {
agents = [].concat(agents);
var record = {
enabled: enabled,
name: name,
description: description,
numAgents: agents.length
numAgents: agents.length,
showOnRegistration: showOnRegistration
};
_.extend(record, extraData);
if (_id) {
this.update({ _id: _id }, { $set: record });
} else {

@ -74,7 +74,7 @@ class LivechatDepartmentAgents extends RocketChat.models._Base {
var agents = this.findByDepartmentId(departmentId).fetch();
if (agents.length === 0) {
return;
return [];
}
var onlineUsers = RocketChat.models.Users.findOnlineUserFromList(_.pluck(agents, 'username'));
@ -93,7 +93,7 @@ class LivechatDepartmentAgents extends RocketChat.models._Base {
if (depAgents) {
return depAgents;
} else {
return null;
return [];
}
}

@ -0,0 +1,12 @@
function updateSandstormMetaData(msg) {
return window.parent.postMessage(msg, '*');
}
if (Meteor.settings.public.sandstorm) {
// Set the path of the parent frame when the grain's path changes.
// See https://docs.sandstorm.io/en/latest/developing/path/
FlowRouter.triggers.enter([({ path }) => {
updateSandstormMetaData({ setPath: path });
}]);
}

@ -6,8 +6,8 @@ Package.describe({
});
Package.onUse(function(api) {
api.use([ 'ecmascript', 'rocketchat:lib', 'jalik:ufs' ]);
api.use([ 'ecmascript', 'rocketchat:lib', 'jalik:ufs', 'kadira:flow-router']);
api.addFiles([ 'server/lib.js', 'server/events.js', 'server/powerbox.js' ], 'server');
api.addFiles([ 'client/powerboxListener.js' ], 'client');
api.addFiles([ 'client/powerboxListener.js', 'client/setPath.js' ], 'client');
});

@ -174,6 +174,12 @@ blockquote {
}
.first-unread {
&.message {
padding-top: 20px;
}
&.sequential.message {
padding-top: 20px;
}
.body {
&::before {
content: "";
@ -181,21 +187,24 @@ blockquote {
height: 1px;
position: absolute;
right: 0px;
left: 20px;
left: 0px;
top: 0px;
.transition(background-color, .5s, linear);
height: 16px;
}
&::after {
content: "unread messages";
display: block;
position: absolute;
right: 0px;
top: -4px;
top: 0px;
text-transform: uppercase;
font-size: 8px;
line-height: 10px;
font-size: 12px;
line-height: 16px;
padding: 0 5px;
.transition(color, .5s, linear);
left: 0;
text-align: center;
}
}
}

@ -22,18 +22,11 @@
*/
html {
.custom-scroll(transparent, @custom-scrollbar-color, 0);
.custom-scroll(@transparent-dark, @custom-scrollbar-color, 0);
}
.flex-nav,
.flex-tab .content,
.messages-container .wrapper,
.list-view,
.page-container .content,
.rooms-list,
.scrollable,
.user-view {
.custom-scroll(transparent, @custom-scrollbar-color);
* {
.custom-scroll(@transparent-dark, @custom-scrollbar-color);
}
body {
@ -77,7 +70,7 @@ blockquote:before {
tr {
background-color: @transparent-light;
&:nth-of-type(even) {
background-color: @transparent-lighter;
background-color: @transparent-lightest;
}
}
tr:hover td {
@ -138,7 +131,7 @@ blockquote:before {
pre {
color: @error-color;
border-color: @error-border;
background-color: @transparent-lighter;
background-color: @transparent-lightest;
}
}
@ -313,7 +306,6 @@ blockquote:before {
}
}
.messages-box {
.start {
color: @info-font-color;
@ -329,6 +321,25 @@ blockquote:before {
* Message content
*/
.first-unread {
.body {
&::before {
background: @transparent-darker;
}
&::after {
color: @primary-font-color;
}
}
}
.first-unread-opaque {
.body {
&::before {
background: @transparent-dark;
}
}
}
.message {
.body {
color: @primary-font-color;
@ -468,22 +479,25 @@ a:hover {
.rooms-list {
background-color: lighten(@primary-background-color, 2.5%);
}
.more:hover,
h3:hover,
li:hover,
.more:hover,
.selected-users li {
background-color: @transparent-darker;
}
li.active .opt {
color: @tertiary-font-color;
li.active {
background-color: @transparent-light !important;
}
.active a {
color: @primary-background-contrast;
background-color: @transparent-lighter;
i {
color: @transparent-lighter;
}
.open-room:hover {
background-color: @transparent-lighter;
.status-offline {
color: @transparent-lighter !important;
}
.has-alert a {
.opt i:hover {
color: @transparent-lightest;
}
.has-alert .name {
color: @primary-background-contrast;
}
.unread {
@ -493,6 +507,12 @@ a:hover {
background-color: @primary-action-color;
color: @primary-action-contrast;
}
.button {
.buttonColors(@tertiary-font-color, mix(@primary-action-color, @primary-background-color));
}
.options button {
.buttonColors(@tertiary-font-color, @primary-background-color);
}
}
@ -502,11 +522,12 @@ a:hover {
.flex-tab {
background-color: @secondary-background-color;
.content, .user-view, .list-view {
.content,
.user-view,
.list-view {
background-color: @secondary-background-color;
}
.message {
background-color: @transparent-lighter;
&.new-day::before {
background-color: @secondary-background-color;
}
@ -577,7 +598,7 @@ i.status-away {
.account-box .status-away .thumb:after,
.account-box .status.away:after,
.options .status .online:after,
.options .status .away:after,
.popup-user-status-away,
.status-pending:after,
.user-image.status-away .avatar:after {
@ -591,7 +612,7 @@ i.status-busy {
.account-box .status-busy .thumb:after,
.account-box .status.busy:after,
.options .status .online:after,
.options .status .busy:after,
.popup-user-status-busy,
.status-busy:after,
.user-image.status-busy .avatar:after {
@ -605,7 +626,7 @@ i.status-offline {
.account-box .status-offline .thumb:after,
.account-box .status.offline:after,
.options .status .online:after,
.options .status .offline:after,
.popup-user-status-offline,
.status-offline:after,
.user-image.status-offline .avatar:after {
@ -685,15 +706,6 @@ i.status-offline {
}
}
.side-nav {
.button {
.buttonColors(@tertiary-font-color, mix(@primary-action-color, @primary-background-color));
}
.options button {
.buttonColors(@tertiary-font-color, @primary-background-color);
}
}
/** ----------------------------------------------------------------------------
* Feedback and overlay content

@ -10,9 +10,10 @@
# Defined range of transparencies reduces random colour variances
alphaColors=
'transparent-darker': 'rgba(0,0,0,0.15)'
'transparent-dark': 'rgba(0,0,0,0.03)'
'transparent-light': 'rgba(255,255,255,0.60)'
'transparent-lighter': 'rgba(255,255,255,0.25)'
'transparent-dark': 'rgba(0,0,0,0.05)'
'transparent-light': 'rgba(255,255,255,0.10)'
'transparent-lighter': 'rgba(255,255,255,0.30)'
'transparent-lightest': 'rgba(255,255,255,0.60)'
# Major colors form the core of the scheme
# Names changed to reflect usage, comments show pre-refactor names
@ -33,10 +34,10 @@ majorColors=
# Minor colours implement major colours by default, but can be overruled
minorColors=
'tertiary-background-color': '@component-color'
'tertiary-font-color': '@transparent-light'
'tertiary-font-color': '@transparent-lightest'
'link-font-color': '@primary-action-color'
'info-font-color': '@secondary-font-color'
'custom-scrollbar-color': '@transparent-dark'
'custom-scrollbar-color': '@transparent-darker'
'status-online': '@success-color'
'status-away': '@pending-color'
'status-busy': '@error-color'

@ -5,7 +5,7 @@ RocketChat.TempSettings = TempSettings
updateColorComponent = ->
$('input.minicolors').minicolors
theme: 'rocketchat'
format: 'hex'
format: 'rgb'
opacity: true
Template.admin.onCreated ->

@ -29,6 +29,20 @@ Template.uploadedFilesList.helpers
url: ->
return '/file-upload/' + @_id + '/' + @name
fixCordova: (url) ->
if Meteor.isCordova and url?[0] is '/'
url = Meteor.absoluteUrl().replace(/\/$/, '') + url
query = "rc_uid=#{Meteor.userId()}&rc_token=#{Meteor._localStorage.getItem('Meteor.loginToken')}"
if url.indexOf('?') is -1
url = url + '?' + query
else
url = url + '&' + query
if Meteor.settings.public.sandstorm or url.match /^(https?:)?\/\//i
return url
else
return Meteor.absoluteUrl().replace(/\/$/, '') + __meteor_runtime_config__.ROOT_URL_PATH_PREFIX + url
Template.uploadedFilesList.events
'click .room-file-item': (e, t) ->
if $(e.currentTarget).siblings('.icon-picture').length

@ -10,10 +10,10 @@
{{#if canDelete}}
<i class="icon-trash file-delete"></i>
{{/if}}
<a title="{{escapedName}}" href="{{url}}" target="_blank" class="file-download" download="">
<a title="{{escapedName}}" href="{{fixCordova url}}" target="_blank" class="file-download" download="">
<i class="icon-download file-download"></i>
</a>
<a title="{{escapedName}}" href="{{url}}" target="_blank" class="room-file-item file-name {{customClassForFileType}}">
<a title="{{escapedName}}" href="{{fixCordova url}}" target="_blank" class="room-file-item file-name {{customClassForFileType}}">
<i class="{{getFileIcon type}}"></i>
<p>{{name}}</p>
</a>

@ -59,6 +59,9 @@ Template.accountBox.events
AccountBox.openFlex()
'click .account-box-item': ->
if @href
FlowRouter.go @href
if @sideNav?
SideNav.setFlex @sideNav
SideNav.openFlex()

@ -19,7 +19,7 @@
<button data-status="offline" class="status offline"><span>{{_ "Invisible"}}</span></button>
<button id="account" class='account-link'><i class="icon-sliders"></i><span>{{_ "My_Account"}}</span></button>
{{#each registeredMenus}}
<a href="{{pathFor href}}" class="account-box-item"><i class="{{icon}}"></i><span>{{name}}</span></a>
<button class="account-box-item"><i class="{{icon}}"></i><span>{{name}}</span></button>
{{/each}}
{{#if showAdminOption }}
<button id="admin" class='account-link'><i class="icon-wrench"></i><span>{{_ "Administration"}}</span></button>

@ -0,0 +1,12 @@
RocketChat.Migrations.add({
version: 67,
up: function() {
if (RocketChat && RocketChat.models && RocketChat.models.LivechatDepartment) {
RocketChat.models.LivechatDepartment.model.update({}, {
$set: {
showOnRegistration: true
}
}, { multi: true });
}
}
});
Loading…
Cancel
Save