diff --git a/services/web/app/coffee/Features/Email/EmailHandler.coffee b/services/web/app/coffee/Features/Email/EmailHandler.coffee new file mode 100644 index 0000000000..5430db7473 --- /dev/null +++ b/services/web/app/coffee/Features/Email/EmailHandler.coffee @@ -0,0 +1,14 @@ +EmailTemplator = require "./EmailTemplator" +EmailSender = require "./EmailSender" + +module.exports = + + sendEmail : (emailType, opts, callback)-> + email = EmailTemplator.buildEmail emailType, opts + opts.html = email.html + opts.subject = email.subject + EmailSender.sendEmail opts, (err)-> + callback(err) + +# module.exports.sendEmail "welcome", {first_name:"henry", to:"henry.oswald@gmail.com"}, -> + diff --git a/services/web/app/coffee/Features/Email/EmailSender.coffee b/services/web/app/coffee/Features/Email/EmailSender.coffee new file mode 100644 index 0000000000..9e9de012a1 --- /dev/null +++ b/services/web/app/coffee/Features/Email/EmailSender.coffee @@ -0,0 +1,33 @@ +logger = require('logger-sharelatex') +metrics = require('../../infrastructure/Metrics') +Settings = require('settings-sharelatex') +metrics = require("../../infrastructure/Metrics") +ses = require('node-ses') + +if Settings.ses?.key? and Settings.ses?.key != "" and Settings.ses?.secret? and Settings.ses?.secret != "" + client = ses.createClient({ key: Settings.ses.key, secret: Settings.ses.secret }); +else + logger.warn "AWS SES credentials are not configured. No emails will be sent." + client = + sendemail: (options, callback = (err, data, res) ->) -> + logger.log options: options, "would send email if SES credentials enabled" + callback() + +module.exports = + + sendEmail : (options, callback = (error) ->)-> + logger.log receiver:options.receiver, subject:options.subject, "sending email" + metrics.inc "email" + options = + to: options.to + from: "ShareLaTeX " + subject: options.subject + message: options.html + replyTo: options.replyTo || "team@sharelatex.com" + client.sendemail options, (err, data, res)-> + if err? + logger.err err:err, "error sending message" + else + logger.log "Message sent to #{options.to}" + callback(err) + \ No newline at end of file diff --git a/services/web/app/coffee/Features/Email/EmailTemplator.coffee b/services/web/app/coffee/Features/Email/EmailTemplator.coffee new file mode 100644 index 0000000000..b249fb49a7 --- /dev/null +++ b/services/web/app/coffee/Features/Email/EmailTemplator.coffee @@ -0,0 +1,85 @@ +_ = require('underscore') +_.templateSettings = + interpolate: /\{\{(.+?)\}\}/g + +PersonalEmailLayout = require("./Layouts/PersonalEmailLayout") +NotificationEmailLayout = require("./Layouts/NotificationEmailLayout") + +templates = {} + +templates.welcome = + subject: _.template "Welcome to ShareLaTeX" + layout: PersonalEmailLayout + compiledTemplate: _.template ''' +Hi {{first_name}}, thanks for signing up to ShareLaTeX. If you ever get lost, you can log in again here. +

+I’m the co-founder of ShareLaTeX and I love talking to our users about our service. Please feel free to get in touch by replying to this email and I will get back to you within a day. + +

+ +Regards,
+Henry
+ShareLaTeX Co-founder +''' + +templates.canceledSubscription = + subject: _.template "ShareLaTeX thoughts" + layout: PersonalEmailLayout + compiledTemplate: _.template ''' +Hi {{first_name}}, + +I am sorry to see you cancelled your premium account. I wondered if you would mind giving me some advice on what the site is lacking at the moment? Criticism from our users about what is missing is the best thing for us to help improve the tool. + +Thank you in advance. + +

+Henry
+ShareLaTeX Co-founder +''' + +templates.passwordReset = + subject: _.template "Password Reset - ShareLatex.com" + layout: NotificationEmailLayout + compiledTemplate: _.template ''' +

Password Reset

+

+Your password has been reset, the new password is

{{newPassword}}: +

+please login here and then change your password in your user settings + +

+

Thank you

+

ShareLatex.com

+''' + +templates.projectSharedWithYou = + subject: _.template "{{owner.email}} wants to share {{project.name}} with you" + layout: NotificationEmailLayout + compiledTemplate: _.template ''' +

{{owner.email}} wants to share '{{project.name}}' with you

+

 

+
+
+
+ + + View Project + + +
+
+
+

Thank you

+

ShareLatex.com

+''' + +module.exports = + + buildEmail: (templateName, opts)-> + template = templates[templateName] + opts.body = template.compiledTemplate(opts) + return { + subject : template.subject(opts) + html: template.layout(opts) + } + diff --git a/services/web/app/coffee/Features/Email/Layouts/NotificationEmailLayout.coffee b/services/web/app/coffee/Features/Email/Layouts/NotificationEmailLayout.coffee new file mode 100644 index 0000000000..6c03b23df6 --- /dev/null +++ b/services/web/app/coffee/Features/Email/Layouts/NotificationEmailLayout.coffee @@ -0,0 +1,384 @@ +_ = require("underscore") +_.templateSettings = + interpolate: /\{\{(.+?)\}\}/g + +module.exports = _.template ''' + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + +
+ + + + + +
+ + + + + +
+ +
+ + + + + +
+ + + + + + +
+
+ {{body}} +
+
+ + +
+ +
+ + + + + +
+ + + + + + +
+ +  follow on Twitter | friend on Facebook + +
+ + +
+ +
+
+
+
+ + + +''' \ No newline at end of file diff --git a/services/web/app/coffee/Features/Email/Layouts/PersonalEmailLayout.coffee b/services/web/app/coffee/Features/Email/Layouts/PersonalEmailLayout.coffee new file mode 100644 index 0000000000..99fb182b36 --- /dev/null +++ b/services/web/app/coffee/Features/Email/Layouts/PersonalEmailLayout.coffee @@ -0,0 +1,19 @@ +_ = require("underscore") +_.templateSettings = + interpolate: /\{\{(.+?)\}\}/g + +module.exports = _.template ''' + + + + + + + + + + {{body}} + + + +''' \ No newline at end of file diff --git a/services/web/test/UnitTests/coffee/Email/EmailHandlerTests.coffee b/services/web/test/UnitTests/coffee/Email/EmailHandlerTests.coffee new file mode 100644 index 0000000000..7e668415a1 --- /dev/null +++ b/services/web/test/UnitTests/coffee/Email/EmailHandlerTests.coffee @@ -0,0 +1,50 @@ +should = require('chai').should() +SandboxedModule = require('sandboxed-module') +assert = require('assert') +path = require('path') +sinon = require('sinon') +modulePath = path.join __dirname, "../../../../app/js/Features/Email/EmailHandler" +expect = require("chai").expect + +describe "EmailHandler", -> + + beforeEach -> + + @settings = {} + @EmailTemplator = + buildEmail:sinon.stub() + @EmailSender = + sendEmail:sinon.stub() + @EmailHandler = SandboxedModule.require modulePath, requires: + "./EmailTemplator":@EmailTemplator + "./EmailSender":@EmailSender + "settings-sharelatex":@settings + "logger-sharelatex": log:-> + + @html = "hello" + + describe "send email", -> + + it "should use the correct options", (done)-> + @EmailTemplator.buildEmail.returns({html:@html}) + @EmailSender.sendEmail.callsArgWith(1) + + opts = + to: "bob@bob.com" + @EmailHandler.sendEmail "welcome", opts, => + args = @EmailSender.sendEmail.args[0][0] + args.html.should.equal @html + done() + + + + it "should return the erroor", (done)-> + @EmailTemplator.buildEmail.returns({html:@html}) + @EmailSender.sendEmail.callsArgWith(1, "error") + + opts = + to: "bob@bob.com" + subject:"hello bob" + @EmailHandler.sendEmail "welcome", opts, (err)=> + err.should.equal "error" + done() diff --git a/services/web/test/UnitTests/coffee/Email/EmailSenderTests.coffee b/services/web/test/UnitTests/coffee/Email/EmailSenderTests.coffee new file mode 100644 index 0000000000..179f704dcc --- /dev/null +++ b/services/web/test/UnitTests/coffee/Email/EmailSenderTests.coffee @@ -0,0 +1,50 @@ +should = require('chai').should() +SandboxedModule = require('sandboxed-module') +assert = require('assert') +path = require('path') +sinon = require('sinon') +modulePath = path.join __dirname, "../../../../app/js/Features/Email/EmailSender.js" +expect = require("chai").expect + +describe "Email", -> + + beforeEach -> + + @settings = + ses: + key: "key" + secret: "secret" + @sesClient = + sendemail: sinon.stub() + @ses = + createClient: => @sesClient + @sender = SandboxedModule.require modulePath, requires: + 'node-ses': @ses + "settings-sharelatex":@settings + "logger-sharelatex": + log:-> + warn:-> + err:-> + + describe "sendEmail", -> + + it "should set the properties on the email to send", (done)-> + @sesClient.sendemail.callsArgWith(1) + + opts = + to: "bob@bob.com" + subject: "new email" + html: "" + replyTo: "sarah@bob.com" + @sender.sendEmail opts, => + args = @sesClient.sendemail.args[0][0] + args.message.should.equal opts.html + args.to.should.equal opts.to + args.subject.should.equal opts.subject + done() + + it "should return the error", (done)-> + @sesClient.sendemail.callsArgWith(1, "error") + @sender.sendEmail {}, (err)=> + err.should.equal "error" + done() diff --git a/services/web/test/UnitTests/coffee/Email/EmailTemplatorTests.coffee b/services/web/test/UnitTests/coffee/Email/EmailTemplatorTests.coffee new file mode 100644 index 0000000000..6fe2f72b8d --- /dev/null +++ b/services/web/test/UnitTests/coffee/Email/EmailTemplatorTests.coffee @@ -0,0 +1,53 @@ +should = require('chai').should() +SandboxedModule = require('sandboxed-module') +assert = require('assert') +path = require('path') +sinon = require('sinon') +modulePath = path.join __dirname, "../../../../app/js/Features/Email/EmailTemplator" +expect = require("chai").expect +_ = require('underscore') +_.templateSettings = + interpolate: /\{\{(.+?)\}\}/g + +describe "Email Templator ", -> + + beforeEach -> + + @settings = {} + @EmailTemplator = SandboxedModule.require modulePath, requires: + "settings-sharelatex":@settings + "logger-sharelatex": log:-> + + describe "welcomeEmail", -> + + beforeEach -> + @opts = + to:"bob@bob.com" + first_name:"bob" + @email = @EmailTemplator.buildEmail("welcome", @opts) + + it "should insert the first_name into the template", -> + @email.html.indexOf(@opts.first_name).should.not.equal -1 + + it "should not have undefined in it", -> + @email.html.indexOf("undefined").should.equal -1 + + describe "projectSharedWithYou", -> + beforeEach -> + @opts = + to:"bob@bob.com" + first_name:"bob" + owner: + email:"sally@hally.com" + project: + url:"http://www.project.com" + name:"standard project" + @email = @EmailTemplator.buildEmail("projectSharedWithYou", @opts) + + it "should insert the owner email into the template", -> + @email.html.indexOf(@opts.owner.email).should.not.equal -1 + @email.subject.indexOf(@opts.owner.email).should.not.equal -1 + + it "should not have undefined in it", -> + @email.html.indexOf("undefined").should.equal -1 + @email.subject.indexOf("undefined").should.equal -1