Skip to content
Snippets Groups Projects
Commit a7a124b7 authored by Marcelo Schmidt's avatar Marcelo Schmidt
Browse files

Merge branch 'develop' of github.com:RocketChat/Rocket.Chat into develop

parents 9c3870a8 47c6eb29
No related branches found
No related tags found
No related merge requests found
Showing
with 332 additions and 18 deletions
......@@ -175,6 +175,7 @@
"hours" : "hours",
"Incorrect_Password" : "Incorrect Password",
"inline_code" : "inline_code",
"Install_Extension" : "Install Extension",
"Install_FxOs" : "Install Rocket.Chat on your Firefox",
"Install_FxOs_done" : "Great! You can now use Rocket.Chat via the icon on your homescreen. Have fun with Rocket.Chat!",
"Install_FxOs_error" : "Sorry, that did not work as intended! The following error appeared:",
......@@ -356,6 +357,7 @@
"quote" : "quote",
"Recents" : "Recents",
"Record" : "Record",
"Refresh_your_page_after_install_to_enable_screen_sharing" : "Refresh your page after install to enable screen sharing",
"Register" : "Register a new account",
"Registration_Succeeded" : "Registration Succeeded",
"Remember_me" : "Remember me",
......@@ -382,6 +384,7 @@
"SAML_Custom_Provider" : "Custom Provider",
"Save_changes" : "Save changes",
"Save_Mobile_Bandwidth" : "Save Mobile Bandwidth",
"Screen_Share" : "Screen Share",
"Search" : "Search",
"Search_Messages" : "Search Messages",
"Search_settings" : "Search settings",
......@@ -494,9 +497,6 @@
"Username_Change_Disabled" : "Your Rocket.Chat administrator has disabled the changing of usernames",
"Username_description" : "The username is used to allow others to mention you in messages.",
"Username_invalid" : "<strong>%s</strong> is not a valid username,<br/> use only letters, numbers, dots and dashes",
"Screen_Share" : "Screen Share",
"You_need_install_an_extension_to_allow_screen_sharing" : "You need install an extension to allow screen sharing",
"Install_Extension" : "Install Extension",
"Username_title" : "Register username",
"Username_unavaliable" : "<strong>%s</strong> is already in use :(",
"Users" : "Users",
......@@ -516,6 +516,7 @@
"you_are_in_preview_mode_of" : "You are in preview mode of channel #<strong>__room_name__</strong>",
"You_can_change_a_different_avatar_too" : "You can change a different avatar too",
"You_need_confirm_email" : "You need to confirm your email to login!",
"You_need_install_an_extension_to_allow_screen_sharing" : "You need install an extension to allow screen sharing",
"You_should_name_it_to_easily_manage_your_integrations" : "You should name it to easily manage your integrations.",
"You_will_not_be_able_to_recover" : "You will not be able to recover this message!",
"Your_entry_has_been_deleted" : "Your entry has been deleted.",
......
......@@ -269,6 +269,8 @@ RocketChat.models.Messages = new class extends RocketChat.models._Base
createRoomRenamedWithRoomIdRoomNameAndUser: (roomId, roomName, user, extraData) ->
return @createWithTypeRoomIdMessageAndUser 'r', roomId, roomName, user, extraData
createCommandWithRoomIdAndUser: (command, roomId, user, extraData) ->
return @createWithTypeRoomIdMessageAndUser 'command', roomId, command, user, extraData
# REMOVE
removeById: (_id) ->
......
......@@ -35,3 +35,4 @@ reactive-var
accounts-password
standard-minifiers
tap:i18n
kevohagan:sweetalert
......@@ -35,6 +35,7 @@ id-map@1.0.4
jquery@1.11.4
kadira:blaze-layout@2.3.0
kadira:flow-router@2.10.0
kevohagan:sweetalert@1.0.0
konecty:nrr@2.0.2
less@2.5.1
livedata@1.0.15
......
......@@ -30,7 +30,9 @@
ts = new Date
Meteor.call 'loadHistory', rid, ts, limit, undefined, (err, result) ->
ChatMessage.upsert {_id: item._id}, item for item in result?.messages or []
for item in result?.messages or []
if item.t isnt 'command'
ChatMessage.upsert {_id: item._id}, item
room.isLoading.set false
room.loaded += result.messages.length
if result.messages.length < limit
......
@t = (key, replaces...) ->
if _.isObject replaces[0]
return TAPi18n.__ key, replaces
else
return TAPi18n.__ key, { postProcess: 'sprintf', sprintf: replaces }
@tr = (key, options, replaces...) ->
if _.isObject replaces[0]
return TAPi18n.__ key, options, replaces
else
return TAPi18n.__ key, options, { postProcess: 'sprintf', sprintf: replaces }
@isRtl = (language) ->
# https://en.wikipedia.org/wiki/Right-to-left#cite_note-2
return language?.split('-').shift().toLowerCase() in ['ar', 'dv', 'fa', 'he', 'ku', 'ps', 'sd', 'ug', 'ur', 'yi']
......@@ -2,4 +2,9 @@ msgStream = new Meteor.Stream 'messages'
Tracker.autorun ->
if visitor.getRoom()?
msgStream.on visitor.getRoom(), (msg) ->
ChatMessage.upsert { _id: msg._id }, msg
if msg.t is 'command'
if msg.msg is 'survey'
unless $('body #survey').length
Blaze.render(Template.survey, $('body').get(0))
else
ChatMessage.upsert { _id: msg._id }, msg
......@@ -43,6 +43,65 @@ input:focus {
box-shadow: 0 0 0;
}
.button {
&:extend(.unselectable);
display: inline-block;
padding: 9px 12px;
font-weight: 500;
font-size: 13px;
margin: 4px;
text-transform: uppercase;
word-spacing: 0;
box-shadow: 1px 1px 0 rgba(0, 0, 0, 0.125);
border: none;
line-height: 16px;
position: relative;
cursor: pointer;background-color: #FFF;
color: rgba(255, 255, 255, 0.85);
background-color: lighten(desaturate(@primary-background-color, 15%), 12.5%);
span {
position: relative;
z-index: 2;
}
&:before {
background-color: rgba(0, 0, 0, 0.1);
content: " ";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
z-index: 1;
.transition(opacity .1s ease-out);
}
&:hover {
text-decoration: none;
color: #FFF;
&:before {
opacity: 1;
}
}
&.secondary {
background-color: @tertiary-background-color;
color: @primary-font-color;
&:before {
background-color: rgba(0, 0, 0, 0.045);
}
}
&.clean {
font-size: 14px;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.08);
&.primary {
font-weight: 600;
}
}
&.button-block {
display: block;
width: 100%;
}
}
.livechat-room {
display: flex;
flex-direction: column;
......@@ -353,6 +412,64 @@ input:focus {
}
}
#survey {
.overlay {
background-color: rgba(0,0,0,0.5);
position: fixed;
height: 100%;
width: 100%;
.wrapper {
background: white;
position: fixed;
height: 60vh;
width: 60vw;
top: 20vh;
left: 20vw;
border-radius: 6px;
display: flex;
flex-direction: column;
header {
flex: 1 0 40px;
padding: 0 15px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
line-height: 40px;
}
.content {
overflow-y: scroll;
padding: 10px;
flex: 1 1 100%;
.instructions {
margin-top: 5px;
}
.survey-item {
margin-top: 20px;
.question {
display: block;
}
.answer {
margin-right: 5px;
}
}
}
footer {
flex: 1 0 60px;
border-top: 1px solid rgba(0, 0, 0, 0.1);
line-height: 60px;
text-align: right;
padding-right: 20px;
}
}
}
}
@media all and(max-height: 200px) {
.livechat-room {
.title {
......
<template name="survey">
<div id="survey">
<div class="overlay">
<div class="wrapper">
<header>
<b>{{_ 'Please_answer_survey'}}</b>
</header>
<div class="content">
<p class="instructions">{{_ 'Survey_instructions'}}</p>
<form id="survey" class="livechat-form">
<div class="survey-item">
<label class="question">
{{_ "How_satisfied_were_you_with_this_chat"}}
</label>
<label class="answer"><input type="radio" name="satisfaction" value="1" />1</label>
<label class="answer"><input type="radio" name="satisfaction" value="2" />2</label>
<label class="answer"><input type="radio" name="satisfaction" value="3" />3</label>
<label class="answer"><input type="radio" name="satisfaction" value="4" />4</label>
<label class="answer"><input type="radio" name="satisfaction" value="5" />5</label>
</div>
<div class="survey-item">
<label class="question">
{{_ "How_knowledgeable_was_the_chat_agent"}}
</label>
<label class="answer"><input type="radio" name="agentKnowledge" value="1" />1</label>
<label class="answer"><input type="radio" name="agentKnowledge" value="2" />2</label>
<label class="answer"><input type="radio" name="agentKnowledge" value="3" />3</label>
<label class="answer"><input type="radio" name="agentKnowledge" value="4" />4</label>
<label class="answer"><input type="radio" name="agentKnowledge" value="5" />5</label>
</div>
<div class="survey-item">
<label class="question">
{{_ "How_responsive_was_the_chat_agent"}}
</label>
<label class="answer"><input type="radio" name="agentResposiveness" value="1" />1</label>
<label class="answer"><input type="radio" name="agentResposiveness" value="2" />2</label>
<label class="answer"><input type="radio" name="agentResposiveness" value="3" />3</label>
<label class="answer"><input type="radio" name="agentResposiveness" value="4" />4</label>
<label class="answer"><input type="radio" name="agentResposiveness" value="5" />5</label>
</div>
<div class="survey-item">
<label class="question">
{{_ "How_friendly_was_the_chat_agent"}}
</label>
<label class="answer"><input type="radio" name="agentFriendliness" value="1" />1</label>
<label class="answer"><input type="radio" name="agentFriendliness" value="2" />2</label>
<label class="answer"><input type="radio" name="agentFriendliness" value="3" />3</label>
<label class="answer"><input type="radio" name="agentFriendliness" value="4" />4</label>
<label class="answer"><input type="radio" name="agentFriendliness" value="5" />5</label>
</div>
<div class="survey-item">
<label class="question">
{{_ "Additional_Feedback"}}
</label>
<textarea name="additionalFeedback" rows="5"></textarea>
</div>
</form>
</div>
<footer>
<button type="button" class="button secondary skip"><span>{{_ "Skip"}}</span></button>
<button type="button" class="button send"><span>{{_ "Send"}}</span></button>
</footer>
</div>
</div>
</div>
</template>
Template.survey.events({
'click button.skip': function(e, instance) {
instance.$('#survey').remove();
},
'click button.send': function(e, instance) {
formData = instance.$('form').serializeArray();
Meteor.call('livechat:saveSurveyFeedback', visitor.getToken(), visitor.getRoom(), formData, function(err, results) {
instance.$('#survey').remove();
swal({
title: t('Thank_you_for_your_feedback'),
type: 'success',
timer: 2000
});
});
}
})
{
"Additional_Feedback": "Additional Feedback",
"Skip": "Skip",
"E-mail": "E-mail",
"Name": "Name",
"How_friendly_was_the_chat_agent": "How friendly was the chat agent?",
"How_knowledgeable_was_the_chat_agent": "How knowledgeable was the chat agent?",
"How_responsive_was_the_chat_agent": "How responsive was the chat agent?",
"How_satisfied_were_you_with_this_chat": "How satisfied were you with this chat?",
"Message": "Message",
"Name": "Name",
"Please_answer_survey": "Please take a moment to answer a quick survey about this chat",
"Please_fill_name_and_email": "Please fill name and e-mail",
"Start_Chat": "Start Chat"
"Send": "Send",
"Start_Chat": "Start Chat",
"Survey": "Survey",
"Survey_instructions": "Rate each question according to your satisfaction, 1 meaning you are completely unsatisfied and 5 meaning you are completely satisfied.",
"Thank_you_for_your_feedback": "Thank you for your feedback",
"User_left" : "Has left the channel."
}
......@@ -52,12 +52,14 @@ Package.onUse(function(api) {
api.addFiles('server/methods/addAgent.js', 'server');
api.addFiles('server/methods/addManager.js', 'server');
api.addFiles('server/methods/saveDepartment.js', 'server');
api.addFiles('server/methods/saveSurveyFeedback.js', 'server');
api.addFiles('server/methods/searchAgent.js', 'server');
api.addFiles('server/methods/removeAgent.js', 'server');
api.addFiles('server/methods/removeManager.js', 'server');
api.addFiles('server/methods/removeDepartment.js', 'server');
// models
api.addFiles('server/models/Users.js', 'server');
api.addFiles('server/models/Rooms.js', 'server');
api.addFiles('server/models/LivechatDepartment.js', 'server');
// collections
......
Meteor.methods({
'livechat:saveSurveyFeedback' (visitorToken, visitorRoom, formData) {
check(visitorToken, String);
check(visitorRoom, String);
check(formData, [Match.ObjectIncluding({ name: String, value: String })]);
console.log('[methods] livechat:saveSurveyFeedback -> '.green, 'arguments:', arguments);
visitor = RocketChat.models.Users.getVisitorByToken(visitorToken);
room = RocketChat.models.Rooms.findOneById(visitorRoom);
if (visitor !== undefined && room !== undefined && room.v !== undefined && visitor.profile !== undefined && room.v.token === visitor.profile.token) {
updateData = {};
for (var item of formData) {
if (_.contains(['satisfaction', 'agentKnowledge', 'agentResposiveness', 'agentFriendliness'], item.name) && _.contains(["1","2","3","4","5"], item.value)) {
updateData[item.name] = item.value;
} else if (item.name === 'additionalFeedback') {
updateData[item.name] = item.value;
}
}
if (!_.isEmpty(updateData)) {
return RocketChat.models.Rooms.updateSurveyFeedbackById(room._id, updateData);
}
}
}
});
/**
* Gets visitor by token
* @param {string} token - Visitor token
*/
RocketChat.models.Rooms.updateSurveyFeedbackById = function(_id, surveyFeedback) {
query = {
_id: _id
};
update = {
$set: {
surveyFeedback: surveyFeedback
}
};
return this.update(query, update);
};
......@@ -12,3 +12,16 @@ RocketChat.models.Users.setOperator = function(_id, operator) {
return this.update(_id, update);
};
/**
* Gets visitor by token
* @param {string} token - Visitor token
*/
RocketChat.models.Users.getVisitorByToken = function(token, options) {
var query = {
"profile.guest": true,
"profile.token": token
};
return this.findOne(query, options);
};
......@@ -47,7 +47,7 @@
if wrapper?
previousHeight = wrapper.scrollHeight
ChatMessage.upsert {_id: item._id}, item for item in result?.messages or []
ChatMessage.upsert {_id: item._id}, item for item in result?.messages or [] when item.t isnt 'command'
if wrapper?
heightDiff = wrapper.scrollHeight - previousHeight
......
......@@ -109,12 +109,13 @@ RocketChat.Notifications.onUser 'message', (msg) ->
Dep.changed()
msgStream.on openedRooms[typeName].rid, (msg) ->
ChatMessage.upsert { _id: msg._id }, msg
if msg.t isnt 'command'
ChatMessage.upsert { _id: msg._id }, msg
else
Meteor.defer ->
RoomManager.updateMentionsMarksOfRoom typeName
Meteor.defer ->
RoomManager.updateMentionsMarksOfRoom typeName
RocketChat.callbacks.run 'streamMessage', msg
RocketChat.callbacks.run 'streamMessage', msg
RocketChat.Notifications.onRoom openedRooms[typeName].rid, 'deleteMessage', onDeleteMessageStream
......
......@@ -321,6 +321,11 @@ class WebRTCClass
getScreen = (audioStream) =>
if document.cookie.indexOf("rocketchatscreenshare=chrome") is -1 and not window.rocketchatscreenshare?
refresh = ->
swal
type: "warning"
title: TAPi18n.__ "Refresh_your_page_after_install_to_enable_screen_sharing"
swal
type: "warning"
title: TAPi18n.__ "Screen_Share"
......@@ -332,12 +337,14 @@ class WebRTCClass
, (isConfirm) =>
if isConfirm
if @navigator is 'chrome'
chrome.webstore.install undefined, undefined, ->
chrome.webstore.install undefined, refresh, ->
window.open('https://chrome.google.com/webstore/detail/rocketchat-screen-share/nocfbnnmjnndkbipkabodnheejiegccf')
refresh()
else if @navigator is 'firefox'
window.open('https://addons.mozilla.org/en-GB/firefox/addon/rocketchat-screen-share/')
refresh()
return
return onError(false)
getScreenSuccess = (stream) =>
if audioStream?
......@@ -350,7 +357,6 @@ class WebRTCClass
video:
mozMediaSource: 'window'
mediaSource: 'window'
navigator.getUserMedia media, getScreenSuccess, onError
else
ChromeScreenShare.getSourceId (id) =>
......@@ -399,7 +405,11 @@ class WebRTCClass
callback null, @localStream
@getUserMedia @media, onSuccess, @onError
onError = (error) =>
callback false
@onError error
@getUserMedia @media, onSuccess, onError
###
......@@ -458,7 +468,9 @@ class WebRTCClass
if @localStream?
@media.desktop = enabled
delete @localStream
@getLocalUserMedia =>
@getLocalUserMedia (err) =>
if err?
return
@screenShareEnabled.set enabled
@stopAllPeerConnections()
@joinCall()
......
......@@ -18,6 +18,10 @@ Meteor.methods
RocketChat.models.Messages.createUserLeaveWithRoomIdAndUser rid, removedUser
if room.t is 'l'
RocketChat.models.Messages.createCommandWithRoomIdAndUser 'survey', rid, user
if room.u?._id is Meteor.userId()
newOwner = _.without(room.usernames, user.username)[0]
if newOwner?
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment