Skip to content

Commit

Permalink
Merge pull request #1733 from RocketChat/mail-messages
Browse files Browse the repository at this point in the history
Mail messages
  • Loading branch information
engelgabriel committed Dec 22, 2015
2 parents 87eea53 + 55a31e3 commit 13d0558
Show file tree
Hide file tree
Showing 25 changed files with 505 additions and 63 deletions.
1 change: 1 addition & 0 deletions .meteor/packages
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ rocketchat:lib
rocketchat:authorization
rocketchat:autolinker
rocketchat:channel-settings
rocketchat:channel-settings-mail-messages
rocketchat:colors
rocketchat:custom-oauth
rocketchat:emojione
Expand Down
1 change: 1 addition & 0 deletions .meteor/versions
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ rocketchat:assets@0.0.1
rocketchat:authorization@0.0.1
rocketchat:autolinker@0.0.1
rocketchat:channel-settings@0.0.1
rocketchat:channel-settings-mail-messages@0.0.1
rocketchat:colors@0.0.1
rocketchat:cors@0.0.1
rocketchat:custom-oauth@1.0.0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Meteor.startup ->
RocketChat.ChannelSettings.addOption
id: 'mail-messages'
template: 'channelSettingsMailMessages'
validation: ->
return RocketChat.authz.hasAllPermission('mail-messages')

RocketChat.callbacks.add 'roomExit', (mainNode) ->
instance = Blaze.getView($('.messages-box')?[0])?.templateInstance()
instance?.resetSelection(false)
, RocketChat.callbacks.priority.MEDIUM, 'room-exit-mail-messages'
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Template.channelSettingsMailMessages.events
'click button.mail-messages': (e, t) ->
Session.set 'channelSettingsMailMessages', Session.get('openedRoom')
RocketChat.TabBar.setTemplate('mailMessagesInstructions')
view = Blaze.getView($('.messages-box')[0])
view?.templateInstance?().resetSelection?(true)

Template.channelSettingsMailMessages.onCreated ->
view = Blaze.getView($('.messages-box')[0])
view?.templateInstance?().resetSelection?(false)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<template name="channelSettingsMailMessages">
<li>
<label>{{_ "Mail_Messages"}}</label>
<div>
<button type="button" class="button primary mail-messages">{{_ "Choose_messages"}}</button>
</div>
</li>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
Template.mailMessagesInstructions.helpers
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) ->
t.reset()

'click .send': (e, t) ->
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')
this.reset()
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<template name="mailMessagesInstructions">
<div class="content">
<div class="list-view mail-message">
<div class="status">
<h2>{{_ "Mail_Messages"}}</h2>
</div>
<p>{{_ "Mail_Messages_Instructions"}}</p>
<form>
<fieldset>
<div class="input-line double-col">
<label>{{_ "From"}}</label>
<div>{{name}}</div>
<div>{{email}}</div>
</div>
<div class="input-line double-col">
<label>{{_ "To"}}</label>
<div>
<input type="text" name="to" value="" />
</div>
</div>
<div class="input-line double-col">
<label>{{_ "Subject"}}</label>
<div>
<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>
</p>
</div>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"Body" : "Body",
"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",
"Your_email_has_been_queued_for_sending" : "Your email has been queued for sending"
}
51 changes: 51 additions & 0 deletions packages/rocketchat-channel-settings-mail-messages/package.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
Package.describe({
name: 'rocketchat:channel-settings-mail-messages',
version: '0.0.1',
summary: 'Channel Settings - Mail Messages',
git: ''
});

Package.onUse(function(api) {
api.versionsFrom('1.0');

api.use([
'coffeescript',
'templating',
'reactive-var',
'less@2.5.0',
'rocketchat:lib@0.0.1',
'rocketchat:channel-settings',
'momentjs:moment'
]);

api.addFiles([
'client/lib/startup.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/lib/startup.coffee',
'server/methods/mailMessages.coffee'
], 'server');

// TAPi18n
var _ = Npm.require('underscore');
var fs = Npm.require('fs');
tapi18nFiles = _.compact(_.map(fs.readdirSync('packages/rocketchat-channel-settings-mail-messages/i18n'), function(filename) {
if (fs.statSync('packages/rocketchat-channel-settings-mail-messages/i18n/' + filename).size > 16) {
return 'i18n/' + filename;
}
}));
api.use('tap:i18n@1.6.1');
api.imply('tap:i18n');
api.addFiles(tapi18nFiles);
});

Package.onTest(function(api) {

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Meteor.startup ->
permission = { _id: 'mail-messages', roles : [ 'admin' ] }
RocketChat.models.Permissions.upsert( permission._id, { $setOnInsert : permission })
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
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")

unless RocketChat.authz.hasPermission(Meteor.userId(), 'mail-messages')
throw new Meteor.Error 'not-authorized'

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

if data.language isnt 'en'
localeFn = Meteor.call 'loadLocale', data.language
if localeFn
Function(localeFn)()

html = ""
RocketChat.models.Messages.findByRoomIdAndMessageIds(data.rid, data.messages, { sort: { ts: 1 } }).forEach (message) ->
dateTime = moment(message.ts).locale(data.language).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
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
RocketChat.ChannelSettings = new class
options = new ReactiveVar {}

###
# Adds an option in Channel Settings
# @config (object)
# id: option id (required)
# template (string): template name to render (required)
# validation (function): if option should be displayed
###
addOption = (config) ->
unless config?.id
throw new Meteor.Error "ChannelSettings-addOption-error", "Option id was not informed."

Tracker.nonreactive ->
opts = options.get()
opts[config.id] = config
options.set opts

getOptions = ->
allOptions = _.toArray options.get()
allowedOptions = _.compact _.map allOptions, (option) ->
if not option.validation? or option.validation()
return option

return _.sortBy allowedOptions, 'order'

addOption: addOption
getOptions: getOptions
Original file line number Diff line number Diff line change
@@ -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;
}
}
}

Expand All @@ -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;
}
}
}
Loading

0 comments on commit 13d0558

Please sign in to comment.