diff --git a/.meteor/packages b/.meteor/packages
index 16ac606c7c0b..2e0df8c465da 100644
--- a/.meteor/packages
+++ b/.meteor/packages
@@ -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
diff --git a/.meteor/versions b/.meteor/versions
index 179bf18b1d60..eef1815c7a41 100644
--- a/.meteor/versions
+++ b/.meteor/versions
@@ -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
diff --git a/packages/rocketchat-channel-settings-mail-messages/client/lib/startup.coffee b/packages/rocketchat-channel-settings-mail-messages/client/lib/startup.coffee
new file mode 100644
index 000000000000..9cf0b6220749
--- /dev/null
+++ b/packages/rocketchat-channel-settings-mail-messages/client/lib/startup.coffee
@@ -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'
diff --git a/packages/rocketchat-channel-settings-mail-messages/client/stylesheets/mail-messages.less b/packages/rocketchat-channel-settings-mail-messages/client/stylesheets/mail-messages.less
new file mode 100644
index 000000000000..6b5e9dd4a5b6
--- /dev/null
+++ b/packages/rocketchat-channel-settings-mail-messages/client/stylesheets/mail-messages.less
@@ -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;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/rocketchat-channel-settings-mail-messages/client/views/channelSettingsMailMessages.coffee b/packages/rocketchat-channel-settings-mail-messages/client/views/channelSettingsMailMessages.coffee
new file mode 100644
index 000000000000..855a105f0f4c
--- /dev/null
+++ b/packages/rocketchat-channel-settings-mail-messages/client/views/channelSettingsMailMessages.coffee
@@ -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)
diff --git a/packages/rocketchat-channel-settings-mail-messages/client/views/channelSettingsMailMessages.html b/packages/rocketchat-channel-settings-mail-messages/client/views/channelSettingsMailMessages.html
new file mode 100644
index 000000000000..10b1cce82fe7
--- /dev/null
+++ b/packages/rocketchat-channel-settings-mail-messages/client/views/channelSettingsMailMessages.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/packages/rocketchat-channel-settings-mail-messages/client/views/mailMessagesInstructions.coffee b/packages/rocketchat-channel-settings-mail-messages/client/views/mailMessagesInstructions.coffee
new file mode 100644
index 000000000000..ed30d5437652
--- /dev/null
+++ b/packages/rocketchat-channel-settings-mail-messages/client/views/mailMessagesInstructions.coffee
@@ -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()
diff --git a/packages/rocketchat-channel-settings-mail-messages/client/views/mailMessagesInstructions.html b/packages/rocketchat-channel-settings-mail-messages/client/views/mailMessagesInstructions.html
new file mode 100644
index 000000000000..c8c27429f6d9
--- /dev/null
+++ b/packages/rocketchat-channel-settings-mail-messages/client/views/mailMessagesInstructions.html
@@ -0,0 +1,44 @@
+
+
+
+
+
{{_ "Mail_Messages"}}
+
+
{{_ "Mail_Messages_Instructions"}}
+
+
+ {{_ "Mail_Message_Missing_to"}}
+
+
+ {{_ "Mail_Message_Invalid_emails" erroredEmails}}
+
+
+ {{{_ "Mail_Message_No_messages_selected_select_all"}}}
+
+
+
+
+
+
+
+
diff --git a/packages/rocketchat-channel-settings-mail-messages/i18n/en.i18n.json b/packages/rocketchat-channel-settings-mail-messages/i18n/en.i18n.json
new file mode 100644
index 000000000000..32ec45e9cdb5
--- /dev/null
+++ b/packages/rocketchat-channel-settings-mail-messages/i18n/en.i18n.json
@@ -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 select all 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"
+}
diff --git a/packages/rocketchat-channel-settings-mail-messages/package.js b/packages/rocketchat-channel-settings-mail-messages/package.js
new file mode 100644
index 000000000000..b0fb444889dd
--- /dev/null
+++ b/packages/rocketchat-channel-settings-mail-messages/package.js
@@ -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) {
+
+});
diff --git a/packages/rocketchat-channel-settings-mail-messages/server/lib/startup.coffee b/packages/rocketchat-channel-settings-mail-messages/server/lib/startup.coffee
new file mode 100644
index 000000000000..0a30dd195e49
--- /dev/null
+++ b/packages/rocketchat-channel-settings-mail-messages/server/lib/startup.coffee
@@ -0,0 +1,3 @@
+Meteor.startup ->
+ permission = { _id: 'mail-messages', roles : [ 'admin' ] }
+ RocketChat.models.Permissions.upsert( permission._id, { $setOnInsert : permission })
diff --git a/packages/rocketchat-channel-settings-mail-messages/server/methods/mailMessages.coffee b/packages/rocketchat-channel-settings-mail-messages/server/methods/mailMessages.coffee
new file mode 100644
index 000000000000..499543047cd7
--- /dev/null
+++ b/packages/rocketchat-channel-settings-mail-messages/server/methods/mailMessages.coffee
@@ -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 += "
#{message.u.username} #{dateTime}
" + RocketChat.Message.parse(message, data.language) + "
"
+
+ 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
diff --git a/packages/rocketchat-channel-settings/client/lib/ChannelSettings.coffee b/packages/rocketchat-channel-settings/client/lib/ChannelSettings.coffee
new file mode 100644
index 000000000000..f9322fbb446e
--- /dev/null
+++ b/packages/rocketchat-channel-settings/client/lib/ChannelSettings.coffee
@@ -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
diff --git a/packages/rocketchat-channel-settings/client/stylesheets/channel-settings.less b/packages/rocketchat-channel-settings/client/stylesheets/channel-settings.less
index a09d3ee179c2..872c1a733183 100644
--- a/packages/rocketchat-channel-settings/client/stylesheets/channel-settings.less
+++ b/packages/rocketchat-channel-settings/client/stylesheets/channel-settings.less
@@ -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;
- }
- }
-}
diff --git a/packages/rocketchat-channel-settings/client/views/channelSettings.coffee b/packages/rocketchat-channel-settings/client/views/channelSettings.coffee
index 7f5639384fe1..7c60aae1705e 100644
--- a/packages/rocketchat-channel-settings/client/views/channelSettings.coffee
+++ b/packages/rocketchat-channel-settings/client/views/channelSettings.coffee
@@ -7,6 +7,8 @@ Template.channelSettings.helpers
return ChatRoom.findOne(@rid)?.t isnt 'd'
roomType: ->
return ChatRoom.findOne(@rid)?.t
+ channelSettings: ->
+ return RocketChat.ChannelSettings.getOptions()
roomTypeDescription: ->
roomType = ChatRoom.findOne(@rid)?.t
if roomType is 'c'
diff --git a/packages/rocketchat-channel-settings/client/views/channelSettings.html b/packages/rocketchat-channel-settings/client/views/channelSettings.html
index bba2381095c2..f2c870343878 100644
--- a/packages/rocketchat-channel-settings/client/views/channelSettings.html
+++ b/packages/rocketchat-channel-settings/client/views/channelSettings.html
@@ -1,50 +1,53 @@
-
-
-
-
-