Email selected messages

pull/1733/head
Marcelo Schmidt 10 years ago
parent a3d6215dd3
commit 6ad7f4454a
  1. 12
      packages/rocketchat-channel-settings-mail-messages/client/lib/RocketChatChannelSettingsMailMessages.coffee
  2. 24
      packages/rocketchat-channel-settings-mail-messages/client/stylesheets/mail-messages.less
  3. 4
      packages/rocketchat-channel-settings-mail-messages/client/views/channelSettingsMailMessages.html
  4. 74
      packages/rocketchat-channel-settings-mail-messages/client/views/mailMessagesInstructions.coffee
  5. 24
      packages/rocketchat-channel-settings-mail-messages/client/views/mailMessagesInstructions.html
  6. 8
      packages/rocketchat-channel-settings-mail-messages/i18n/en.i18n.json
  7. 10
      packages/rocketchat-channel-settings-mail-messages/package.js
  8. 40
      packages/rocketchat-channel-settings-mail-messages/server/methods/mailMessages.coffee
  9. 30
      packages/rocketchat-channel-settings/client/stylesheets/channel-settings.less
  10. 62
      packages/rocketchat-channel-settings/client/views/channelSettings.html
  11. 26
      packages/rocketchat-lib/lib/Message.coffee
  12. 0
      packages/rocketchat-lib/lib/MessageTypes.coffee
  13. 3
      packages/rocketchat-lib/package.js
  14. 9
      packages/rocketchat-lib/server/models/Messages.coffee

@ -1,12 +0,0 @@
RocketChat.ChannelSettingsMailMessages = new class
addCheckboxes = ->
$('.messages-box .wrapper .message').each (index, item) ->
$item = $(item)
unless $item.find('input[type=checkbox]').length
$item.prepend(HTML.toHTML(HTML.INPUT({type: 'checkbox', class: 'send-message', style: 'position: absolute; left: 0' })));
removeCheckboxes = ->
$('.messages-box .wrapper .message input[type=checkbox].send-message').remove()
addCheckboxes: addCheckboxes
removeCheckboxes: removeCheckboxes

@ -0,0 +1,24 @@
.flex-tab {
.mail-message {
form {
margin-top: 20px;
.input-line.double-col {
margin-bottom: 20px;
label {
line-height: 15px;
}
div {
line-height: 15px;
i.octicon {
font-size: 13px;
opacity: 0.4;
vertical-align: top;
}
}
}
}
}
}

@ -1,8 +1,8 @@
<template name="channelSettingsMailMessages">
<div class="input-line double-col">
<li>
<label>{{_ "Mail_Messages"}}</label>
<div>
<button type="button" class="button primary mail-messages">{{_ "Choose_messages"}}</button>
</div>
</div>
</li>
</template>

@ -1,17 +1,79 @@
Template.mailMessagesInstructions.helpers
body: ->
return ''
name: ->
return Meteor.user().name
email: ->
return Meteor.user().emails?[0]?.address
roomName: ->
return ChatRoom.findOne(Session.get('openedRoom'))?.name
erroredEmails: ->
return Template.instance()?.erroredEmails.get().join(', ')
Template.mailMessagesInstructions.events
'click .cancel': (e, t) ->
RocketChat.TabBar.setTemplate('channelSettings')
view = Blaze.getView($('.messages-box')[0])
view?.templateInstance?().resetSelection?(false)
t.reset()
'click .send': (e, t) ->
console.log 'sending'
t.$('.error').hide()
$btn = t.$('button.send')
oldBtnValue = $btn.html()
$btn.html(TAPi18n.__('Sending'))
selectedMessages = $('.messages-box .message.selected')
error = false
if selectedMessages.length is 0
t.$('.error-select').show()
error = true
if t.$('input[name=to]').val().trim()
rfcMailPatternWithName = /^(?:.*<)?([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)(?:>?)$/
emails = t.$('input[name=to]').val().trim().split(',')
erroredEmails = []
for email in emails
unless rfcMailPatternWithName.test email.trim()
erroredEmails.push email.trim()
t.erroredEmails.set erroredEmails
if erroredEmails.length > 0
t.$('.error-invalid-emails').show()
error = true
else
t.$('.error-missing-to').show()
error = true
if error
$btn.html(oldBtnValue)
else
data =
rid: Session.get('openedRoom')
to: t.$('input[name=to]').val().trim()
subject: t.$('input[name=subject]').val().trim()
messages: selectedMessages.map((i, message) -> return message.id).toArray()
language: localStorage.getItem('userLanguage')
Meteor.call 'mailMessages', data, (err, result) ->
$btn.html(oldBtnValue)
if err?
return toastr.error(err.reason or err.message)
toastr.success(TAPi18n.__('Your_email_has_been_queued_for_sending'))
t.reset()
'click .select-all': (e, t) ->
t.$('.error-select').hide()
view = Blaze.getView($('.messages-box')[0])
view?.templateInstance?().selectedMessages = _.pluck(ChatMessage.find({rid: Session.get('openedRoom')})?.fetch(), '_id')
$(".messages-box .message").addClass('selected')
Template.mailMessagesInstructions.onCreated ->
@erroredEmails = new ReactiveVar []
@reset = ->
RocketChat.TabBar.setTemplate('channelSettings')
view = Blaze.getView($('.messages-box')[0])
view?.templateInstance?().resetSelection?(false)
@autorun =>
if Session.get('channelSettingsMailMessages') isnt Session.get('openedRoom')
RocketChat.TabBar.setTemplate('channelSettings')

@ -1,6 +1,6 @@
<template name="mailMessagesInstructions">
<div class="content">
<div class="list-view">
<div class="list-view mail-message">
<div class="status">
<h2>{{_ "Mail_Messages"}}</h2>
</div>
@ -9,9 +9,8 @@
<fieldset>
<div class="input-line double-col">
<label>{{_ "From"}}</label>
<div>
<input type="text" name="from" value="" />
</div>
<div>{{name}}</div>
<div>{{email}}</div>
</div>
<div class="input-line double-col">
<label>{{_ "To"}}</label>
@ -22,17 +21,20 @@
<div class="input-line double-col">
<label>{{_ "Subject"}}</label>
<div>
<input type="text" name="subject" value="" />
</div>
</div>
<div class="input-line double-col">
<label>{{_ "Body"}}</label>
<div>
{{body}}
<input type="text" name="subject" value="{{_ "Mail_Messages_Subject" roomName}}" />
</div>
</div>
</fieldset>
</form>
<div class="error error-missing-to alert alert-danger" style="display: none">
{{_ "Mail_Message_Missing_to"}}
</div>
<div class="error error-invalid-emails alert alert-danger" style="display: none">
{{_ "Mail_Message_Invalid_emails" erroredEmails}}
</div>
<div class="error error-select alert alert-danger" style="display: none">
{{{_ "Mail_Message_No_messages_selected_select_all"}}}
</div>
<p style="margin-top: 30px">
<button type="button" class="button secondary cancel">{{_ "Cancel"}}</button>
<button type="button" class="button primary send">{{_ "Send"}}</button>

@ -3,9 +3,15 @@
"Cancel" : "Cancel",
"Choose_messages" : "Choose messages",
"From" : "From",
"Mail_Message_Missing_to" : "You must provide one or more To e-mail addresses, separated by commas.",
"Mail_Message_No_messages_selected_select_all" : "You haven't selected any messages. Would you like to <a href='#' class='select-all'>select all</a> visible messages?",
"Mail_Messages" : "Mail Messages",
"Mail_Messages_Instructions" : "Choose which messages you want to send via e-mail by clicking the messages",
"Mail_Messages_Subject" : "Here's a selected portion of %s messages",
"Mail_Message_Invalid_emails" : "You have provided one or more invalid e-mails: %s",
"Send" : "Send",
"Sending" : "Sending...",
"Subject" : "Subject",
"To" : "To"
"To" : "To",
"Your_email_has_been_queued_for_sending" : "Your email has been queued for sending"
}

@ -14,18 +14,24 @@ Package.onUse(function(api) {
'reactive-var',
'less@2.5.0',
'rocketchat:lib@0.0.1',
'rocketchat:channel-settings'
'rocketchat:channel-settings',
'momentjs:moment'
]);
api.addFiles([
'client/lib/ChannelSettings.coffee',
'client/lib/RocketChatChannelSettingsMailMessages.coffee',
'client/stylesheets/mail-messages.less',
'client/views/channelSettingsMailMessages.html',
'client/views/channelSettingsMailMessages.coffee',
'client/views/mailMessagesInstructions.html',
'client/views/mailMessagesInstructions.coffee'
], 'client');
api.addFiles([
'server/methods/mailMessages.coffee'
], 'server');
// TAPi18n
var _ = Npm.require('underscore');
var fs = Npm.require('fs');

@ -0,0 +1,40 @@
Meteor.methods
'mailMessages': (data) ->
if not Meteor.userId()
throw new Meteor.Error('invalid-user', "[methods] mailMessages -> Invalid user")
check(data, Match.ObjectIncluding({ rid: String, to: String, subject: String, messages: [ String ], language: String }))
room = Meteor.call 'canAccessRoom', data.rid, Meteor.userId()
unless room
throw new Meteor.Error('invalid-room', "[methods] mailMessages -> Invalid room")
rfcMailPatternWithName = /^(?:.*<)?([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)(?:>?)$/
emails = data.to.trim().split(',')
for email in emails
unless rfcMailPatternWithName.test email.trim()
throw new Meteor.Error('invalid-email', "[methods] mailMessages -> Invalid e-mail")
user = Meteor.user()
name = user.name
email = user.emails?[0]?.address
moment(data.language)
html = ""
RocketChat.models.Messages.findByRoomIdAndMessageIds(data.rid, data.messages, { sort: { ts: 1 } }).forEach (message) ->
dateTime = moment(message.ts).format('L LT')
html += "<p style='margin-bottom: 5px'><b>#{message.u.username}</b> <span style='color: #aaa; font-size: 12px'>#{dateTime}</span><br />" + RocketChat.Message.parse(message, data.language) + "</p>"
Meteor.defer ->
Email.send
to: emails
from: RocketChat.settings.get('From_Email')
replyTo: email
subject: data.subject
html: html
console.log 'Sending email to ' + emails.join(', ')
return true

@ -1,11 +1,25 @@
.flex-tab {
.channel-settings {
margin-top: 60px;
padding: 20px;
ul {
li {
margin-bottom: 20px;
}
}
form {
label {
display: block;
font-weight: bold;
margin-bottom: 5px;
}
div span {
font-size: 14px;
i.octicon {
font-size: 12px;
vertical-align: middle;
margin-left: 3px;
}
}
}
@ -19,15 +33,3 @@
}
}
}
.input-line.double-col {
div {
line-height: 15px;
padding: 10px 20px 10px 0;
i.octicon {
font-size: 13px;
opacity: 0.4;
vertical-align: top;
}
}
}

@ -1,36 +1,32 @@
<template name="channelSettings">
<div class="control">
<div class="header">
<h2>{{_ "Room_Info"}}</h2>
</div>
</div>
<div class="channel-settings scrollable">
<form>
<fieldset>
{{#if notDirect}}
<div class="input-line double-col">
<div class="content">
<div class="list-view channel-settings">
<div class="status">
<h2>{{_ "Room_Info"}}</h2>
</div>
<form>
<ul class="list clearfix">
<li>
<label>{{_ "Name"}}</label>
<div>
{{#if editing 'roomName'}}
<input type="text" name="roomName" value="{{roomName}}" class="editing" /> <button type="button" class="button secondary cancel">{{_ "Cancel"}}</button> <button type="button" class="button primary save">{{_ "Save"}}</button>
{{else}}
{{roomName}}{{#if canEdit}} <i class="octicon octicon-pencil" data-edit="roomName"></i>{{/if}}
<span>{{roomName}}{{#if canEdit}} <i class="octicon octicon-pencil" data-edit="roomName"></i>{{/if}}</span>
{{/if}}
</div>
</div>
{{/if}}
<div class="input-line double-col">
<label>{{_ "Topic"}}</label>
<div>
{{#if editing 'roomTopic'}}
<input type="text" name="roomTopic" value="{{roomTopic}}" class="editing" /> <button type="button" class="button secondary cancel">{{_ "Cancel"}}</button> <button type="button" class="button primary save">{{_ "Save"}}</button>
{{else}}
{{roomTopic}}{{#if canEdit}} <i class="octicon octicon-pencil" data-edit="roomTopic"></i>{{/if}}
{{/if}}
</div>
</div>
{{#if notDirect}}
<div class="input-line double-col">
</li>
<li>
<label>{{_ "Topic"}}</label>
<div>
{{#if editing 'roomTopic'}}
<input type="text" name="roomTopic" value="{{roomTopic}}" class="editing" /> <button type="button" class="button secondary cancel">{{_ "Cancel"}}</button> <button type="button" class="button primary save">{{_ "Save"}}</button>
{{else}}
<span>{{roomTopic}}{{#if canEdit}} <i class="octicon octicon-pencil" data-edit="roomTopic"></i>{{/if}}</span>
{{/if}}
</div>
</li>
<li>
<label>{{_ "Type"}}</label>
<div>
{{#if editing 'roomType'}}
@ -39,15 +35,15 @@
<button type="button" class="button secondary cancel">{{_ "Cancel"}}</button>
<button type="button" class="button primary save">{{_ "Save"}}</button>
{{else}}
{{roomTypeDescription}}{{#if canEdit}} <i class="octicon octicon-pencil" data-edit="roomType"></i>{{/if}}
<span>{{roomTypeDescription}}{{#if canEdit}} <i class="octicon octicon-pencil" data-edit="roomType"></i>{{/if}}</span>
{{/if}}
</div>
</div>
{{/if}}
{{#each channelSettings}}
{{> Template.dynamic template=template data=data}}
{{/each}}
</fieldset>
</form>
</li>
{{#each channelSettings}}
{{> Template.dynamic template=template data=data}}
{{/each}}
</ul>
</form>
</div>
</div>
</template>

@ -0,0 +1,26 @@
RocketChat.Message =
parse: (msg, language) ->
messageType = RocketChat.MessageTypes.getType(msg)
if messageType?.render?
return messageType.render(msg)
else if messageType?.template?
# render template
else if messageType?.message?
if not language and localStorage?.getItem('userLanguage')
language = localStorage.getItem('userLanguage')
if messageType.data?(msg)?
return TAPi18n.__(messageType.message, messageType.data(msg), language)
else
return TAPi18n.__(messageType.message, {}, language)
else
if msg.u?.username is RocketChat.settings.get('Chatops_Username')
msg.html = msg.msg
return msg.html
msg.html = msg.msg
if _.trim(msg.html) isnt ''
msg.html = _.escapeHTML msg.html
# message = RocketChat.callbacks.run 'renderMessage', msg
msg.html = msg.html.replace /\n/gm, '<br/>'
return msg.html

@ -33,6 +33,8 @@ Package.onUse(function(api) {
api.addFiles('lib/settings.coffee');
api.addFiles('lib/callbacks.coffee');
api.addFiles('lib/slashCommand.coffee');
api.addFiles('lib/Message.coffee');
api.addFiles('lib/MessageTypes.coffee');
// SERVER LIB
api.addFiles('server/lib/RateLimiter.coffee', 'server');
@ -92,7 +94,6 @@ Package.onUse(function(api) {
api.addFiles('client/Notifications.coffee', 'client');
api.addFiles('client/TabBar.coffee', 'client');
api.addFiles('client/MessageAction.coffee', 'client');
api.addFiles('client/MessageTypes.coffee', 'client');
// VERSION
api.addFiles('rocketchat.info');

@ -116,6 +116,14 @@ RocketChat.models.Messages = new class extends RocketChat.models._Base
return @find(query, options)?.fetch?()?[0]?.ts
findByRoomIdAndMessageIds: (rid, messageIds, options) ->
query =
rid: rid
_id:
$in: messageIds
return @find query, options
cloneAndSaveAsHistoryById: (_id) ->
me = RocketChat.models.Users.findOneById Meteor.userId()
record = @findOneById _id
@ -134,7 +142,6 @@ RocketChat.models.Messages = new class extends RocketChat.models._Base
return @insert record
# UPDATE
setHiddenById: (_id, hidden=true) ->
query =

Loading…
Cancel
Save