diff --git a/packages/rocketchat-api/server/routes.coffee b/packages/rocketchat-api/server/routes.coffee index 10b34d05b93..116ea410be0 100644 --- a/packages/rocketchat-api/server/routes.coffee +++ b/packages/rocketchat-api/server/routes.coffee @@ -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: -> diff --git a/packages/rocketchat-authorization/server/startup.coffee b/packages/rocketchat-authorization/server/startup.coffee index a6beed9bbd3..323a7b1c271 100644 --- a/packages/rocketchat-authorization/server/startup.coffee +++ b/packages/rocketchat-authorization/server/startup.coffee @@ -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'] } diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index b5c67698f63..0a90f0fed11 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -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" -} \ No newline at end of file +} diff --git a/packages/rocketchat-i18n/i18n/pt.i18n.json b/packages/rocketchat-i18n/i18n/pt.i18n.json index cbea202314d..9c38197eda9 100644 --- a/packages/rocketchat-i18n/i18n/pt.i18n.json +++ b/packages/rocketchat-i18n/i18n/pt.i18n.json @@ -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" -} \ No newline at end of file +} diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index 398052a8c27..301f538d447 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -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'); diff --git a/packages/rocketchat-lib/server/functions/settings.coffee b/packages/rocketchat-lib/server/functions/settings.coffee index e5197b6f20f..104896b7d46 100644 --- a/packages/rocketchat-lib/server/functions/settings.coffee +++ b/packages/rocketchat-lib/server/functions/settings.coffee @@ -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 diff --git a/packages/rocketchat-lib/server/methods/cleanChannelHistory.js b/packages/rocketchat-lib/server/methods/cleanChannelHistory.js new file mode 100644 index 00000000000..8c96388c7fb --- /dev/null +++ b/packages/rocketchat-lib/server/methods/cleanChannelHistory.js @@ -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 + } + }); + } + } +}); diff --git a/packages/rocketchat-lib/server/methods/getChannelHistory.js b/packages/rocketchat-lib/server/methods/getChannelHistory.js new file mode 100644 index 00000000000..03958a40fae --- /dev/null +++ b/packages/rocketchat-lib/server/methods/getChannelHistory.js @@ -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 + }; + } +}); diff --git a/packages/rocketchat-lib/server/models/Messages.coffee b/packages/rocketchat-lib/server/models/Messages.coffee index 02cc0f77e56..a117c8ada37 100644 --- a/packages/rocketchat-lib/server/models/Messages.coffee +++ b/packages/rocketchat-lib/server/models/Messages.coffee @@ -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: diff --git a/packages/rocketchat-livechat/app/client/lib/_livechat.js b/packages/rocketchat-livechat/app/client/lib/_livechat.js index 46895402b43..f1c361bcbdc 100644 --- a/packages/rocketchat-livechat/app/client/lib/_livechat.js +++ b/packages/rocketchat-livechat/app/client/lib/_livechat.js @@ -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); + } + } })(); diff --git a/packages/rocketchat-livechat/app/client/lib/chatMessages.coffee b/packages/rocketchat-livechat/app/client/lib/chatMessages.coffee index f63f91e5ce2..4b6eecccf0e 100644 --- a/packages/rocketchat-livechat/app/client/lib/chatMessages.coffee +++ b/packages/rocketchat-livechat/app/client/lib/chatMessages.coffee @@ -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 diff --git a/packages/rocketchat-livechat/app/client/lib/commands.js b/packages/rocketchat-livechat/app/client/lib/commands.js index 8c8f60bbbdd..decd3173f69 100644 --- a/packages/rocketchat-livechat/app/client/lib/commands.js +++ b/packages/rocketchat-livechat/app/client/lib/commands.js @@ -60,5 +60,9 @@ this.Commands = { showConfirmButton: false }); } + }, + + connected: function() { + Livechat.connecting = false; } }; diff --git a/packages/rocketchat-livechat/app/client/lib/hooks.js b/packages/rocketchat-livechat/app/client/lib/hooks.js index 4a8fa387a3f..6733cb2f6ef 100644 --- a/packages/rocketchat-livechat/app/client/lib/hooks.js +++ b/packages/rocketchat-livechat/app/client/lib/hooks.js @@ -19,6 +19,14 @@ var api = { if (theme.fontColor) { Livechat.customFontColor = theme.fontColor; } + }, + + setDepartment: function(department) { + Livechat.department = department; + }, + + clearDepartment: function() { + Livechat.department = null; } }; diff --git a/packages/rocketchat-livechat/app/client/stylesheets/loading.less b/packages/rocketchat-livechat/app/client/stylesheets/loading.less new file mode 100644 index 00000000000..5d7fd0b884b --- /dev/null +++ b/packages/rocketchat-livechat/app/client/stylesheets/loading.less @@ -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); + } +} \ No newline at end of file diff --git a/packages/rocketchat-livechat/app/client/views/loading.html b/packages/rocketchat-livechat/app/client/views/loading.html new file mode 100644 index 00000000000..69e145bcdff --- /dev/null +++ b/packages/rocketchat-livechat/app/client/views/loading.html @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/packages/rocketchat-livechat/app/client/views/messages.html b/packages/rocketchat-livechat/app/client/views/messages.html index 0f8004e3fac..e3fc9775508 100644 --- a/packages/rocketchat-livechat/app/client/views/messages.html +++ b/packages/rocketchat-livechat/app/client/views/messages.html @@ -16,17 +16,21 @@ +
+ +
+ + +
+

{{_ "Agents"}}

diff --git a/packages/rocketchat-livechat/client/views/app/livechatDepartmentForm.js b/packages/rocketchat-livechat/client/views/app/livechatDepartmentForm.js index ec40ba225e1..4b3825a8caa 100644 --- a/packages/rocketchat-livechat/client/views/app/livechatDepartmentForm.js +++ b/packages/rocketchat-livechat/client/views/app/livechatDepartmentForm.js @@ -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 = []; diff --git a/packages/rocketchat-livechat/client/views/app/livechatDepartments.html b/packages/rocketchat-livechat/client/views/app/livechatDepartments.html index 4da6e4e5fec..8854dd299ec 100644 --- a/packages/rocketchat-livechat/client/views/app/livechatDepartments.html +++ b/packages/rocketchat-livechat/client/views/app/livechatDepartments.html @@ -3,10 +3,11 @@ - - - - + + + + + @@ -17,6 +18,7 @@ + {{/each}} diff --git a/packages/rocketchat-livechat/config.js b/packages/rocketchat-livechat/config.js index 1270237727a..80102c9613e 100644 --- a/packages/rocketchat-livechat/config.js +++ b/packages/rocketchat-livechat/config.js @@ -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' } + }); + }); diff --git a/packages/rocketchat-livechat/server/lib/Livechat.js b/packages/rocketchat-livechat/server/lib/Livechat.js index 34af055d948..37a52e7b8f6 100644 --- a/packages/rocketchat-livechat/server/lib/Livechat.js +++ b/packages/rocketchat-livechat/server/lib/Livechat.js @@ -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; + } } }; diff --git a/packages/rocketchat-livechat/server/lib/QueueMethods.js b/packages/rocketchat-livechat/server/lib/QueueMethods.js index 85013f3a4fe..c40fe6166ca 100644 --- a/packages/rocketchat-livechat/server/lib/QueueMethods.js +++ b/packages/rocketchat-livechat/server/lib/QueueMethods.js @@ -120,4 +120,4 @@ RocketChat.QueueMethods = { return room; } -}; +}; \ No newline at end of file diff --git a/packages/rocketchat-livechat/server/methods/takeInquiry.js b/packages/rocketchat-livechat/server/methods/takeInquiry.js index b431c5721ea..8a110f76ecc 100644 --- a/packages/rocketchat-livechat/server/methods/takeInquiry.js +++ b/packages/rocketchat-livechat/server/methods/takeInquiry.js @@ -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; } diff --git a/packages/rocketchat-livechat/server/models/LivechatDepartment.js b/packages/rocketchat-livechat/server/models/LivechatDepartment.js index 08627f550ef..a1f9285f0ff 100644 --- a/packages/rocketchat-livechat/server/models/LivechatDepartment.js +++ b/packages/rocketchat-livechat/server/models/LivechatDepartment.js @@ -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 { diff --git a/packages/rocketchat-livechat/server/models/LivechatDepartmentAgents.js b/packages/rocketchat-livechat/server/models/LivechatDepartmentAgents.js index 681a6c73eec..ef725815b9c 100644 --- a/packages/rocketchat-livechat/server/models/LivechatDepartmentAgents.js +++ b/packages/rocketchat-livechat/server/models/LivechatDepartmentAgents.js @@ -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 []; } } diff --git a/packages/rocketchat-sandstorm/client/setPath.js b/packages/rocketchat-sandstorm/client/setPath.js new file mode 100644 index 00000000000..004ab4cee8e --- /dev/null +++ b/packages/rocketchat-sandstorm/client/setPath.js @@ -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 }); + }]); +} diff --git a/packages/rocketchat-sandstorm/package.js b/packages/rocketchat-sandstorm/package.js index a052ac15b38..bd68220fa2e 100644 --- a/packages/rocketchat-sandstorm/package.js +++ b/packages/rocketchat-sandstorm/package.js @@ -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'); }); diff --git a/packages/rocketchat-theme/assets/stylesheets/base.less b/packages/rocketchat-theme/assets/stylesheets/base.less index 58c23c015f7..c7f9d670ad3 100644 --- a/packages/rocketchat-theme/assets/stylesheets/base.less +++ b/packages/rocketchat-theme/assets/stylesheets/base.less @@ -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; } } } diff --git a/packages/rocketchat-theme/assets/stylesheets/utils/_colors.import.less b/packages/rocketchat-theme/assets/stylesheets/utils/_colors.import.less index c06d30225d6..99dd5f2d9ca 100755 --- a/packages/rocketchat-theme/assets/stylesheets/utils/_colors.import.less +++ b/packages/rocketchat-theme/assets/stylesheets/utils/_colors.import.less @@ -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 diff --git a/packages/rocketchat-theme/server/variables.coffee b/packages/rocketchat-theme/server/variables.coffee index 75631d4044b..b5ebd79e2c0 100755 --- a/packages/rocketchat-theme/server/variables.coffee +++ b/packages/rocketchat-theme/server/variables.coffee @@ -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' diff --git a/packages/rocketchat-ui-admin/admin/admin.coffee b/packages/rocketchat-ui-admin/admin/admin.coffee index 12a1d056607..08aba89d9ad 100644 --- a/packages/rocketchat-ui-admin/admin/admin.coffee +++ b/packages/rocketchat-ui-admin/admin/admin.coffee @@ -5,7 +5,7 @@ RocketChat.TempSettings = TempSettings updateColorComponent = -> $('input.minicolors').minicolors theme: 'rocketchat' - format: 'hex' + format: 'rgb' opacity: true Template.admin.onCreated -> diff --git a/packages/rocketchat-ui-flextab/flex-tab/tabs/uploadedFilesList.coffee b/packages/rocketchat-ui-flextab/flex-tab/tabs/uploadedFilesList.coffee index e0196df020a..d470222c5a3 100644 --- a/packages/rocketchat-ui-flextab/flex-tab/tabs/uploadedFilesList.coffee +++ b/packages/rocketchat-ui-flextab/flex-tab/tabs/uploadedFilesList.coffee @@ -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 diff --git a/packages/rocketchat-ui-flextab/flex-tab/tabs/uploadedFilesList.html b/packages/rocketchat-ui-flextab/flex-tab/tabs/uploadedFilesList.html index 4d8a25c79b0..2f5ad0bc782 100644 --- a/packages/rocketchat-ui-flextab/flex-tab/tabs/uploadedFilesList.html +++ b/packages/rocketchat-ui-flextab/flex-tab/tabs/uploadedFilesList.html @@ -10,10 +10,10 @@ {{#if canDelete}} {{/if}} - + - +

{{name}}

diff --git a/packages/rocketchat-ui-sidenav/side-nav/accountBox.coffee b/packages/rocketchat-ui-sidenav/side-nav/accountBox.coffee index 7d5e89e70cb..b0f2bf2741b 100644 --- a/packages/rocketchat-ui-sidenav/side-nav/accountBox.coffee +++ b/packages/rocketchat-ui-sidenav/side-nav/accountBox.coffee @@ -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() diff --git a/packages/rocketchat-ui-sidenav/side-nav/accountBox.html b/packages/rocketchat-ui-sidenav/side-nav/accountBox.html index 5f79fea9a0b..48c93b0003c 100644 --- a/packages/rocketchat-ui-sidenav/side-nav/accountBox.html +++ b/packages/rocketchat-ui-sidenav/side-nav/accountBox.html @@ -19,7 +19,7 @@ {{#each registeredMenus}} - {{name}} + {{/each}} {{#if showAdminOption }} diff --git a/server/startup/migrations/v067.js b/server/startup/migrations/v067.js new file mode 100644 index 00000000000..86a10fd7417 --- /dev/null +++ b/server/startup/migrations/v067.js @@ -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 }); + } + } +});
{{_ "Name"}}{{_ "Description"}}{{_ "Num_Agents"}}{{_ "Enabled"}}{{_ "Name"}}{{_ "Description"}}{{_ "Num_Agents"}}{{_ "Enabled"}}{{_ "Show_on_registration"}} {{_ "Delete"}}
{{description}} {{numAgents}} {{#if enabled}}{{_ "Yes"}}{{else}}{{_ "No"}}{{/if}}{{#if showOnRegistration}}{{_ "Yes"}}{{else}}{{_ "No"}}{{/if}}