Skip to content
Snippets Groups Projects
Unverified Commit 3de6e3e6 authored by Diego Sampaio's avatar Diego Sampaio
Browse files

Merge branch 'develop' into sidenav-roomlist

parents cb77b33f c52c18f9
No related merge requests found
......@@ -132,7 +132,7 @@ rocketchat:videobridge
rocketchat:webrtc
rocketchat:wordpress
rocketchat:message-snippet
rocketchat:google-natural-language
#rocketchat:google-natural-language
rocketchat:drupal
rocketchat:monitoring
#rocketchat:chatops
......
......@@ -148,7 +148,6 @@ rocketchat:file@0.0.1
rocketchat:file-upload@0.0.1
rocketchat:github-enterprise@0.0.1
rocketchat:gitlab@0.0.1
rocketchat:google-natural-language@0.0.1
rocketchat:highlight-words@0.0.1
rocketchat:i18n@0.0.1
rocketchat:iframe-login@1.0.0
......
......@@ -57,7 +57,7 @@
},
"aws-sign2": {
"version": "0.6.0",
"resolved": "http://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
"from": "aws-sign2@>=0.6.0 <0.7.0"
},
"aws4": {
......@@ -167,7 +167,7 @@
},
"core-util-is": {
"version": "1.0.2",
"resolved": "http://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"from": "core-util-is@>=1.0.0 <1.1.0"
},
"create-error-class": {
......@@ -1372,7 +1372,7 @@
"dependencies": {
"caseless": {
"version": "0.11.0",
"resolved": "http://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz",
"from": "caseless@>=0.11.0 <0.12.0"
},
"har-validator": {
......@@ -1501,7 +1501,7 @@
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "http://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"from": "util-deprecate@>=1.0.1 <1.1.0"
},
"uuid": {
......
......@@ -229,6 +229,18 @@ class ModelRooms extends RocketChat.models._Base {
return this.find(query, options);
}
findByNameAndTypeNotDefault(name, type, options) {
const query = {
t: type,
name,
default: {
$ne: true
}
};
return this.find(query, options);
}
findByNameAndTypeNotContainingUsername(name, type, username, options) {
const query = {
t: type,
......
export const VRecDialog = new class {
static initClass() {
this.prototype.opened = false;
this.prototype.initiated = false;
this.prototype.width = 400;
this.prototype.height = 280;
constructor() {
this.opened = false;
this.initiated = false;
this.width = 400;
this.height = 280;
}
init() {
......
import moment from 'moment'
import toastr from 'toastr'
class @ChatMessages
init: (node) ->
this.editing = {}
this.records = {}
this.messageMaxSize = RocketChat.settings.get('Message_MaxAllowedSize')
this.wrapper = $(node).find(".wrapper")
this.input = $(node).find(".input-message").get(0)
this.$input = $(this.input)
this.hasValue = new ReactiveVar false
this.bindEvents()
return
resize: ->
dif = (if RocketChat.Layout.isEmbedded() then 0 else 60) + $(".messages-container").find("footer").outerHeight()
dif += if $(".announcement").length > 0 then 40 else 0
$(".messages-box").css
height: "calc(100% - #{dif}px)"
getEditingIndex: (element) ->
msgs = this.wrapper.get(0).querySelectorAll(".own:not(.system)")
index = 0
for msg in msgs
if msg is element
return index
index++
return -1
recordInputAsDraft: () ->
id = this.editing.id
message = this.getMessageById id
record = this.records[id] || {}
draft = this.input.value
if(draft is message.msg)
this.clearCurrentDraft()
else
record.draft = draft
this.records[id] = record
getMessageDraft: (id) ->
return this.records[id]
clearMessageDraft: (id) ->
delete this.records[id]
clearCurrentDraft: () ->
this.clearMessageDraft this.editing.id
resetToDraft: (id) ->
message = this.getMessageById id
old_value = this.input.value
this.input.value = message.msg
return old_value isnt message.msg
getMessageById: (id) ->
return ChatMessage.findOne(id)
toPrevMessage: ->
index = this.editing.index
this.editByIndex if index? then index - 1 else undefined
toNextMessage: ->
index = this.editing.index
this.clearEditing() unless this.editByIndex index + 1
editByIndex: (index) ->
return false if not this.editing.element and index?
msgs = this.wrapper.get(0).querySelectorAll(".own:not(.system)")
index = msgs.length - 1 if not index?
return false unless msgs[index]
element = msgs[index]
this.edit element, index
return true
edit: (element, index) ->
index = this.getEditingIndex(element) if not index?
message = this.getMessageById element.getAttribute("id")
hasPermission = RocketChat.authz.hasAtLeastOnePermission('edit-message', message.rid)
editAllowed = RocketChat.settings.get 'Message_AllowEditing'
editOwn = message?.u?._id is Meteor.userId()
return unless hasPermission or (editAllowed and editOwn)
return if element.classList.contains("system")
blockEditInMinutes = RocketChat.settings.get 'Message_AllowEditing_BlockEditInMinutes'
if blockEditInMinutes? and blockEditInMinutes isnt 0
msgTs = moment(message.ts) if message.ts?
currentTsDiff = moment().diff(msgTs, 'minutes') if msgTs?
if currentTsDiff > blockEditInMinutes
return
msg = this.getMessageDraft(message._id)?.draft
msg = message.msg unless msg?
editingNext = this.editing.index < index
old_input = this.input.value
this.clearEditing()
this.hasValue.set true
this.editing.element = element
this.editing.index = index
this.editing.id = message._id
element.classList.add("editing")
this.input.classList.add("editing")
this.$input.closest('.message-form').addClass('editing')
this.input.focus()
if message.attachments? and message.attachments[0].description?
this.input.value = message.attachments[0].description
else
this.input.value = msg
cursor_pos = if editingNext then 0 else -1
this.$input.setCursorPosition(cursor_pos)
clearEditing: ->
if this.editing.element
this.recordInputAsDraft()
this.editing.element.classList.remove("editing")
this.input.classList.remove("editing")
this.$input.closest('.message-form').removeClass('editing')
delete this.editing.id
delete this.editing.element
delete this.editing.index
this.input.value = this.editing.saved or ""
cursor_pos = this.editing.savedCursor ? -1
this.$input.setCursorPosition(cursor_pos)
this.hasValue.set this.input.value isnt ''
else
this.editing.saved = this.input.value
this.editing.savedCursor = this.input.selectionEnd
###*
# * @param {string} rim room ID
# * @param {Element} input DOM element
# * @param {function?} done callback
###
send: (rid, input, done = ->) ->
if _.trim(input.value) isnt ''
readMessage.enable()
readMessage.readNow()
$('.message.first-unread').removeClass('first-unread')
msg = input.value
msgObject = { _id: Random.id(), rid: rid, msg: msg}
# Run to allow local encryption, and maybe other client specific actions to be run before send
RocketChat.promises.run('onClientBeforeSendMessage', msgObject).then (msgObject) =>
# checks for the final msgObject.msg size before actually sending the message
if this.isMessageTooLong(msgObject.msg)
return toastr.error t('Message_too_long')
this.clearCurrentDraft()
if this.editing.id
this.update(this.editing.id, rid, msgObject.msg)
return
KonchatNotification.removeRoomNotification(rid)
input.value = ''
input.updateAutogrow?()
this.hasValue.set false
this.stopTyping(rid)
#Check if message starts with /command
if msg[0] is '/'
match = msg.match(/^\/([^\s]+)(?:\s+(.*))?$/m)
if match?
if RocketChat.slashCommands.commands[match[1]]
commandOptions = RocketChat.slashCommands.commands[match[1]]
command = match[1]
param = if match[2]? then match[2] else ''
if commandOptions.clientOnly
commandOptions.callback(command, param, msgObject)
else
Meteor.call 'slashCommand', {cmd: command, params: param, msg: msgObject }, (err, result) -> commandOptions.result?(err, result, {cmd: command, params: param, msg: msgObject })
return
if !RocketChat.settings.get('Message_AllowUnrecognizedSlashCommand')
invalidCommandMsg =
_id: Random.id()
rid: rid
ts: new Date
msg: TAPi18n.__('No_such_command', { command: match[1] })
u:
username: "rocketbot"
private: true
ChatMessage.upsert { _id: invalidCommandMsg._id }, invalidCommandMsg
return
Meteor.call 'sendMessage', msgObject
done()
# If edited message was emptied we ask for deletion
else if this.editing.element
message = this.getMessageById this.editing.id
if message.attachments? and message.attachments[0].description?
this.update(this.editing.id, rid, '', true)
return
# Restore original message in textbox in case delete is canceled
this.resetToDraft this.editing.id
this.confirmDeleteMsg message, done
confirmDeleteMsg: (message, done = ->) ->
return if RocketChat.MessageTypes.isSystemMessage(message)
swal {
title: t('Are_you_sure')
text: t('You_will_not_be_able_to_recover')
type: 'warning'
showCancelButton: true
confirmButtonColor: '#DD6B55'
confirmButtonText: t('Yes_delete_it')
cancelButtonText: t('Cancel')
closeOnConfirm: false
html: false
}, =>
swal
title: t('Deleted')
text: t('Your_entry_has_been_deleted')
type: 'success'
timer: 1000
showConfirmButton: false
if this.editing.id is message._id
this.clearEditing message
this.deleteMsg message
this.$input.focus()
done()
# In order to avoid issue "[Callback not called when still animating](https://github.com/t4t5/sweetalert/issues/528)"
$('.sweet-alert').addClass 'visible'
deleteMsg: (message) ->
forceDelete = RocketChat.authz.hasAtLeastOnePermission('force-delete-message', message.rid)
blockDeleteInMinutes = RocketChat.settings.get 'Message_AllowDeleting_BlockDeleteInMinutes'
if blockDeleteInMinutes? and blockDeleteInMinutes isnt 0 and forceDelete is false
msgTs = moment(message.ts) if message.ts?
currentTsDiff = moment().diff(msgTs, 'minutes') if msgTs?
if currentTsDiff > blockDeleteInMinutes
toastr.error(t('Message_deleting_blocked'))
return
Meteor.call 'deleteMessage', { _id: message._id }, (error, result) ->
if error
return handleError(error)
pinMsg: (message) ->
message.pinned = true
Meteor.call 'pinMessage', message, (error, result) ->
if error
return handleError(error)
unpinMsg: (message) ->
message.pinned = false
Meteor.call 'unpinMessage', message, (error, result) ->
if error
return handleError(error)
update: (id, rid, msg, isDescription) ->
if _.trim(msg) isnt '' or isDescription is true
Meteor.call 'updateMessage', { _id: id, msg: msg, rid: rid }
this.clearEditing()
this.stopTyping(rid)
startTyping: (rid, input) ->
if _.trim(input.value) isnt ''
MsgTyping.start(rid)
else
MsgTyping.stop(rid)
stopTyping: (rid) ->
MsgTyping.stop(rid)
bindEvents: ->
if this.wrapper?.length
$(".input-message").autogrow
postGrowCallback: =>
this.resize()
tryCompletion: (input) ->
value = input.value.match(/[^\s]+$/)
if value?.length > 0
value = value[0]
re = new RegExp value, 'i'
user = Meteor.users.findOne username: re
if user?
input.value = input.value.replace value, "@#{user.username} "
keyup: (rid, event) ->
input = event.currentTarget
k = event.which
keyCodes = [
13, # Enter
20, # Caps lock
16, # Shift
9, # Tab
27, # Escape Key
17, # Control Key
91, # Windows Command Key
19, # Pause Break
18, # Alt Key
93, # Right Click Point Key
45, # Insert Key
34, # Page Down
35, # Page Up
144, # Num Lock
145 # Scroll Lock
]
keyCodes.push i for i in [35..40] # Home, End, Arrow Keys
keyCodes.push i for i in [112..123] # F1 - F12
unless k in keyCodes
this.startTyping(rid, input)
this.hasValue.set input.value isnt ''
keydown: (rid, event) ->
sendOnEnter = Meteor.user()?.settings?.preferences?.sendOnEnter
input = event.currentTarget
$input = $(input)
k = event.which
this.resize(input)
if k is 13
if not sendOnEnter? || sendOnEnter is 'normal' || (sendOnEnter is 'desktop' and Meteor.Device.isDesktop())
if not event.shiftKey and not event.ctrlKey and not event.altKey and not event.metaKey # Enter without shift/ctrl/alt
event.preventDefault()
event.stopPropagation()
this.send(rid, input)
return
else if not event.shiftKey
return input.value +='\n'
else if sendOnEnter is 'alternative'
if event.shiftKey or event.ctrlKey or event.altKey or event.metaKey # Enter with shift/ctrl/alt
event.preventDefault()
event.stopPropagation()
this.send(rid, input)
return
if k is 9 # Tab
event.preventDefault()
event.stopPropagation()
@tryCompletion input
if k is 27 # Escape
if this.editing.index?
record = this.getMessageDraft(this.editing.id)
# If resetting did nothing then edited message is same as original
unless this.resetToDraft this.editing.id
this.clearCurrentDraft()
this.clearEditing()
event.preventDefault()
event.stopPropagation()
return
else if k is 38 or k is 40 # Arrow Up or down
return true if event.shiftKey
cursor_pos = input.selectionEnd
if k is 38 # Arrow Up
if cursor_pos is 0
this.toPrevMessage()
else if not event.altKey
return true
this.$input.setCursorPosition(0) if event.altKey
else # Arrow Down
if cursor_pos is input.value.length
this.toNextMessage()
else if not event.altKey
return true
this.$input.setCursorPosition(-1) if event.altKey
return false
# ctrl (command) + shift + k -> clear room messages
else if k is 75 and ((navigator?.platform?.indexOf('Mac') isnt -1 and event.metaKey and event.shiftKey) or (navigator?.platform?.indexOf('Mac') is -1 and event.ctrlKey and event.shiftKey))
RoomHistoryManager.clear rid
valueChanged: (rid, event) ->
if this.input.value.length is 1
this.determineInputDirection()
determineInputDirection: () ->
this.input.dir = if this.isMessageRtl(this.input.value) then 'rtl' else 'ltr'
# http://stackoverflow.com/a/14824756
isMessageRtl: (message) ->
ltrChars = 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF'+'\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF'
rtlChars = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC'
rtlDirCheck = new RegExp "^[^#{ltrChars}]*[#{rtlChars}]"
return rtlDirCheck.test message
isMessageTooLong: (message) ->
message?.length > this.messageMaxSize
isEmpty: ->
return !this.hasValue.get()
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