commit
13d0558bfa
@ -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' |
@ -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; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -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) |
@ -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> |
@ -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() |
@ -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> |
@ -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" |
||||
} |
@ -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) { |
||||
|
||||
}); |
@ -0,0 +1,3 @@ |
||||
Meteor.startup -> |
||||
permission = { _id: 'mail-messages', roles : [ 'admin' ] } |
||||
RocketChat.models.Permissions.upsert( permission._id, { $setOnInsert : permission }) |
@ -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 |
@ -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 |
@ -1,50 +1,53 @@ |
||||
<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"> |
||||
<label>{{_ "Name"}}</label> |
||||
<div class="content"> |
||||
<div class="list-view channel-settings"> |
||||
<div class="status"> |
||||
<h2>{{_ "Room_Info"}}</h2> |
||||
</div> |
||||
<form> |
||||
<ul class="list clearfix"> |
||||
{{#if notDirect}} |
||||
<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}} |
||||
<span>{{roomName}}{{#if canEdit}} <i class="octicon octicon-pencil" data-edit="roomName"></i>{{/if}}</span> |
||||
{{/if}} |
||||
</div> |
||||
</li> |
||||
{{/if}} |
||||
<li> |
||||
<label>{{_ "Topic"}}</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> |
||||
{{#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}} |
||||
{{roomName}}{{#if canEdit}} <i class="octicon octicon-pencil" data-edit="roomName"></i>{{/if}} |
||||
<span>{{roomTopic}}{{#if canEdit}} <i class="octicon octicon-pencil" data-edit="roomTopic"></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"> |
||||
<label>{{_ "Type"}}</label> |
||||
<div> |
||||
{{#if editing 'roomType'}} |
||||
<label><input type="radio" name="roomType" class="editing" value="c" checked="{{$eq roomType 'c'}}" /> {{_ "Channel"}}</label> |
||||
<label><input type="radio" name="roomType" value="p" checked="{{$eq roomType 'p'}}" /> {{_ "Private_Group"}}</label> |
||||
<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}} |
||||
{{/if}} |
||||
</div> |
||||
</div> |
||||
{{/if}} |
||||
</fieldset> |
||||
</form> |
||||
</li> |
||||
{{#if notDirect}} |
||||
<li> |
||||
<label>{{_ "Type"}}</label> |
||||
<div> |
||||
{{#if editing 'roomType'}} |
||||
<label><input type="radio" name="roomType" class="editing" value="c" checked="{{$eq roomType 'c'}}" /> {{_ "Channel"}}</label> |
||||
<label><input type="radio" name="roomType" value="p" checked="{{$eq roomType 'p'}}" /> {{_ "Private_Group"}}</label> |
||||
<button type="button" class="button secondary cancel">{{_ "Cancel"}}</button> |
||||
<button type="button" class="button primary save">{{_ "Save"}}</button> |
||||
{{else}} |
||||
<span>{{roomTypeDescription}}{{#if canEdit}} <i class="octicon octicon-pencil" data-edit="roomType"></i>{{/if}}</span> |
||||
{{/if}} |
||||
</div> |
||||
</li> |
||||
{{/if}} |
||||
{{#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 |
Loading…
Reference in new issue