From 89dfd3435e340b711702c44ea0721820a6bf7d3d Mon Sep 17 00:00:00 2001 From: Marcelo Schmidt Date: Thu, 28 Apr 2016 15:09:09 -0300 Subject: [PATCH] Add customization options for enrollment and invitation e-mails --- packages/rocketchat-lib/i18n/en.i18n.json | 8 ++++- packages/rocketchat-lib/lib/placeholders.js | 20 ++++++++++++ packages/rocketchat-lib/package.js | 1 + .../server/methods/insertOrUpdateUser.coffee | 30 +++++++++++------ .../server/methods/sendInvitationEmail.coffee | 19 +++++++++-- .../server/startup/settings.coffee | 14 +++++--- .../rocketchat-ui-admin/admin/admin.coffee | 17 ++++------ packages/rocketchat-ui-admin/admin/admin.html | 32 +++++++++++-------- server/lib/accounts.coffee | 31 +++++++++++------- 9 files changed, 118 insertions(+), 54 deletions(-) create mode 100644 packages/rocketchat-lib/lib/placeholders.js diff --git a/packages/rocketchat-lib/i18n/en.i18n.json b/packages/rocketchat-lib/i18n/en.i18n.json index ccae101fe0a..df2101493c4 100644 --- a/packages/rocketchat-lib/i18n/en.i18n.json +++ b/packages/rocketchat-lib/i18n/en.i18n.json @@ -31,6 +31,8 @@ "Accounts_EmailVerification_Description" : "Make sure you have correct SMTP settings to use this feature", "Accounts_Enrollment_Email" : "Enrollment Email", "Accounts_Enrollment_Email_Description" : "You may use the following placeholders:
", + "Accounts_Enrollment_Email_Default" : "

Welcome to

[Site_Name]

Go to [Site_URL] and try the best open source chat solution available today!

", + "Accounts_Enrollment_Email_Subject_Default" : "Welcome to [Site_Name]", "Accounts_Iframe_api_method" : "Api Method", "Accounts_Iframe_api_url" : "API URL", "Accounts_iframe_enabled" : "Enabled", @@ -99,8 +101,9 @@ "Accounts_ShowFormLogin" : "Show form-based Login", "Accounts_UseDefaultBlockedDomainsList" : "Use Default Blocked Domains List", "Accounts_UseDNSDomainCheck" : "Use DNS Domain Check", + "Accounts_UserAddedEmail_Default" : "

Welcome to

[Site_Name]

Go to [Site_URL] and try the best open source chat solution available today!

You may login using your email: [email] and password: [password]. You may be required to change it after your first login.", "Accounts_UserAddedEmail_Description" : "You may use the following placeholders:

", - "Accounts_UserAddedEmail_Subject" : "User Added Welcome Email", + "Accounts_UserAddedEmailSubject_Default" : "Welcome to [Site_Name]", "Activate" : "Activate", "Activity" : "Activity", "Add" : "Add", @@ -283,7 +286,9 @@ "Email_already_exists" : "Email already exists", "Email_body" : "Email body", "Email_Change_Disabled" : "Your Rocket.Chat administrator has disabled the changing of email", + "Email_Header_Description" : "You may use the following placeholders:
", "Email_from" : "From", + "Email_Footer_Description" : "You may use the following placeholders:
", "Email_Notification_Mode" : "Offline Email Notifications", "Email_Notification_Mode_All" : "Every Mention/DM", "Email_Notification_Mode_Disabled" : "Disabled", @@ -400,6 +405,7 @@ "Flags" : "Flags", "Follow_social_profiles" : "Follow our social profiles, fork us on github and share your thoughts about the rocket.chat app on our trello board.", "Food_and_Drink" : "Food & Drink", + "Footer" : "Footer", "For_your_security_you_must_re_enter_your_password_to_continue" : "For your security, you must re-enter your password to continue", "Force_SSL" : "Force SSL", "Force_SSL_Description" : "*Caution!* _Force SSL_ should never be used with reverse proxy. If you have a reverse proxy, you should do the redirect THERE. This option exists for deployments like Heroku, that does not allow the redirect configuration at the reverse proxy.", diff --git a/packages/rocketchat-lib/lib/placeholders.js b/packages/rocketchat-lib/lib/placeholders.js new file mode 100644 index 00000000000..d747927938b --- /dev/null +++ b/packages/rocketchat-lib/lib/placeholders.js @@ -0,0 +1,20 @@ +RocketChat.placeholders = {}; + +RocketChat.placeholders.replace = function(str, data) { + if (!str) { + return ""; + } + + str = str.replace(/\[Site_Name\]/g, RocketChat.settings.get("Site_Name") || ''); + str = str.replace(/\[Site_URL\]/g, RocketChat.settings.get("Site_Url") || ''); + + if (data) { + str = str.replace(/\[name\]/g, data.name || ''); + str = str.replace(/\[fname\]/g, _.strLeft(data.name, ' ') || ''); + str = str.replace(/\[lname\]/g, _.strRightBack(data.name, ' ') || ''); + str = str.replace(/\[email\]/g, data.email || ''); + str = str.replace(/\[password\]/g, data.password || ''); + } + + return str; +}; diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index 6baba59606f..aa3d0c9c8f1 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -45,6 +45,7 @@ Package.onUse(function(api) { api.addFiles('lib/configLogger.coffee'); api.addFiles('lib/callbacks.coffee'); api.addFiles('lib/fileUploadRestrictions.js'); + api.addFiles('lib/placeholders.js'); api.addFiles('lib/promises.coffee'); api.addFiles('lib/slashCommand.coffee'); api.addFiles('lib/Message.coffee'); diff --git a/packages/rocketchat-lib/server/methods/insertOrUpdateUser.coffee b/packages/rocketchat-lib/server/methods/insertOrUpdateUser.coffee index 84eafffc4b8..a522a6a761c 100644 --- a/packages/rocketchat-lib/server/methods/insertOrUpdateUser.coffee +++ b/packages/rocketchat-lib/server/methods/insertOrUpdateUser.coffee @@ -64,20 +64,30 @@ Meteor.methods Meteor.call('joinDefaultChannels'); if userData.sendWelcomeEmail - html = RocketChat.settings.get('Accounts_UserAddedEmail'); - html = html.replace /\[name\]/g, userData.name or '' - html = html.replace /\[fname\]/g, _.strLeft(userData.name, ' ') or '' - html = html.replace /\[lname\]/g, _.strRightBack(userData.name, ' ') or '' - html = html.replace /\[email\]/g, userData.email or '' - html = html.replace /\[password\]/g, userData.password or '' - html = html.replace /\[Site_Name\]/g, RocketChat.settings.get("Site_Name") or '' - html = html.replace /\[Site_URL\]/g, RocketChat.settings.get("Site_Url") or '' + + header = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Header') || "") + footer = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Footer') || "") + + + if RocketChat.settings.get('Accounts_UserAddedEmail_Customized') + subject = RocketChat.settings.get('Accounts_UserAddedEmailSubject') + html = RocketChat.settings.get('Accounts_UserAddedEmail') + else + subject = TAPi18n.__('Accounts_UserAddedEmailSubject_Default', { lng: Meteor.user()?.language || RocketChat.settings.get('language') || 'en' }) + html = TAPi18n.__('Accounts_UserAddedEmail_Default', { lng: Meteor.user()?.language || RocketChat.settings.get('language') || 'en' }) + + subject = RocketChat.placeholders.replace(subject); + html = RocketChat.placeholders.replace(html, { + name: userData.name, + email: userData.email, + password: userData.password + }); email = { to: userData.email from: RocketChat.settings.get('From_Email'), - subject: RocketChat.settings.get('Accounts_UserAddedEmail_Subject') || TAPi18n.__("Welcome_to_the", { lng: RocketChat.settings.get('Language') || "en" }) + (RocketChat.settings.get('Site_Name') || ""), - html: html + subject: subject, + html: header + html + footer }; Email.send(email); diff --git a/packages/rocketchat-lib/server/methods/sendInvitationEmail.coffee b/packages/rocketchat-lib/server/methods/sendInvitationEmail.coffee index 88227e161c9..9be056c3b28 100644 --- a/packages/rocketchat-lib/server/methods/sendInvitationEmail.coffee +++ b/packages/rocketchat-lib/server/methods/sendInvitationEmail.coffee @@ -9,14 +9,27 @@ Meteor.methods rfcMailPattern = /^[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])?)*$/ validEmails = _.compact _.map emails, (email) -> return email if rfcMailPattern.test email + header = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Header') || "") + footer = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Footer') || "") + + if RocketChat.settings.get('Invitation_Customized') + subject = RocketChat.settings.get('Invitation_Subject') + html = RocketChat.settings.get('Invitation_HTML') + else + subject = TAPi18n.__('Invitation_Subject_Default', { lng: Meteor.user()?.language || RocketChat.settings.get('language') || 'en' }) + html = TAPi18n.__('Invitation_HTML_Default', { lng: Meteor.user()?.language || RocketChat.settings.get('language') || 'en' }) + + subject = RocketChat.placeholders.replace(subject); + for email in validEmails @unblock() + html = RocketChat.placeholders.replace(html, { email: email }); + Email.send to: email from: RocketChat.settings.get 'From_Email' - subject: RocketChat.settings.get 'Invitation_Subject' - html: RocketChat.settings.get 'Invitation_HTML' - + subject: subject + html: header + html + footer return validEmails diff --git a/packages/rocketchat-lib/server/startup/settings.coffee b/packages/rocketchat-lib/server/startup/settings.coffee index 5aebbc55ff3..c3ecf86b2d1 100644 --- a/packages/rocketchat-lib/server/startup/settings.coffee +++ b/packages/rocketchat-lib/server/startup/settings.coffee @@ -105,6 +105,10 @@ RocketChat.settings.addGroup 'General', -> RocketChat.settings.addGroup 'Email', -> + @section 'Header and Footer', -> + @add 'Email_Header', '', { type: 'code', code: 'text/html', multiline: true, i18nLabel: 'Header' } + @add 'Email_Footer', '', { type: 'code', code: 'text/html', multiline: true, i18nLabel: 'Footer' } + @section 'SMTP', -> @add 'SMTP_Host', '', { type: 'string', env: true, i18nLabel: 'Host' } @add 'SMTP_Port', '', { type: 'string', env: true, i18nLabel: 'Port' } @@ -119,12 +123,14 @@ RocketChat.settings.addGroup 'Email', -> @add 'Invitation_HTML', '', { type: 'code', code: 'text/html', multiline: true, i18nLabel: 'Body', i18nDescription: 'Invitation_HTML_Description', enableQuery: { _id: 'Invitation_Customized', value: true }, i18nDefaultQuery: { _id: 'Invitation_Customized', value: false } } @section 'Registration', -> - @add 'Accounts_Enrollment_Email_Subject', '', { type: 'string', i18nLabel: 'Subject' } - @add 'Accounts_Enrollment_Email', '', { type: 'code', code: 'text/html', multiline: true, i18nLabel: 'Body', i18nDescription: 'Accounts_Enrollment_Email_Description' } + @add 'Accounts_Enrollment_Customized', false, { type: 'boolean', i18nLabel: 'Custom' } + @add 'Accounts_Enrollment_Email_Subject', '', { type: 'string', i18nLabel: 'Subject', enableQuery: { _id: 'Accounts_Enrollment_Customized', value: true }, i18nDefaultQuery: { _id: 'Accounts_Enrollment_Customized', value: false } } + @add 'Accounts_Enrollment_Email', '', { type: 'code', code: 'text/html', multiline: true, i18nLabel: 'Body', enableQuery: { _id: 'Accounts_Enrollment_Customized', value: true }, i18nDefaultQuery: { _id: 'Accounts_Enrollment_Customized', value: false } } @section 'Registration via Admin', -> - @add 'Accounts_UserAddedEmailSubject', '', { type: 'string', i18nLabel: "Subject" } - @add 'Accounts_UserAddedEmail', '', { type: 'code', code: 'text/html', multiline: true, i18nLabel: 'Body', i18nDescription: 'Accounts_UserAddedEmail_Description' } + @add 'Accounts_UserAddedEmail_Customized', false, { type: 'boolean', i18nLabel: 'Custom' } + @add 'Accounts_UserAddedEmailSubject', '', { type: 'string', i18nLabel: "Subject", enableQuery: { _id: 'Accounts_UserAddedEmail_Customized', value: true }, i18nDefaultQuery: { _id: 'Accounts_UserAddedEmail_Customized', value: false } } + @add 'Accounts_UserAddedEmail', '', { type: 'code', code: 'text/html', multiline: true, i18nLabel: 'Body', i18nDescription: 'Accounts_UserAddedEmail_Description', enableQuery: { _id: 'Accounts_UserAddedEmail_Customized', value: true }, i18nDefaultQuery: { _id: 'Accounts_UserAddedEmail_Customized', value: false } } RocketChat.settings.addGroup 'Message', -> diff --git a/packages/rocketchat-ui-admin/admin/admin.coffee b/packages/rocketchat-ui-admin/admin/admin.coffee index 890273bf3bb..e2e7cad0d40 100644 --- a/packages/rocketchat-ui-admin/admin/admin.coffee +++ b/packages/rocketchat-ui-admin/admin/admin.coffee @@ -52,14 +52,7 @@ Template.admin.helpers found = 0 for item in i18nDefaultQuery if TempSettings.findOne(item)? - if setting.type is 'code' - codeMirrorBox = $('.code-mirror-box[data-editor-id="'+setting._id+'"]') - codeMirrorBox.find('.CodeMirror')[0].CodeMirror.doc.setValue(TAPi18n.__(setting._id + '_Default')) - else - setting.value = TAPi18n.__(setting._id + '_Default') - # else if setting.type is 'code' - # codeMirrorBox = $('.code-mirror-box[data-editor-id="'+setting._id+'"]') - # codeMirrorBox.find('.CodeMirror')[0].CodeMirror.doc.setValue(setting.value) + setting.value = TAPi18n.__(setting._id + '_Default') sections[setting.section or ''] ?= [] sections[setting.section or ''].push setting @@ -72,6 +65,9 @@ Template.admin.helpers return sectionsArray + i18nDefaultValue: -> + return TAPi18n.__(@_id + '_Default') + isDisabled: -> if @blocked return { disabled: 'disabled' } @@ -154,7 +150,7 @@ Template.admin.helpers random: -> return Random.id() - getEditorOptions: -> + getEditorOptions: (readOnly = false) -> return {} = lineNumbers: true mode: this.code or "javascript" @@ -168,6 +164,7 @@ Template.admin.helpers matchTags: true, showTrailingSpace: true highlightSelectionMatches: true + readOnly: readOnly setEditorOnBlur: (_id) -> Meteor.defer -> @@ -302,7 +299,7 @@ Template.admin.events "click .expand": (e) -> $(e.currentTarget).closest('.section').removeClass('section-collapsed') $(e.currentTarget).closest('button').removeClass('expand').addClass('collapse').find('span').text(TAPi18n.__ "Collapse") - $('.code-mirror-box .CodeMirror').each (index, codeMirror) -> + $('.CodeMirror').each (index, codeMirror) -> codeMirror.CodeMirror.refresh() "click .collapse": (e) -> diff --git a/packages/rocketchat-ui-admin/admin/admin.html b/packages/rocketchat-ui-admin/admin/admin.html index fe62d12adc2..5fa88c5e323 100644 --- a/packages/rocketchat-ui-admin/admin/admin.html +++ b/packages/rocketchat-ui-admin/admin/admin.html @@ -91,21 +91,25 @@ {{/if}} {{#if $eq type 'code'}} -
-
- {{label}} -
- {{> CodeMirror name=_id options=getEditorOptions code=value }} - {{setEditorOnBlur _id}} -
- - + {{#if isDisabled.disabled}} + {{> CodeMirror name=_id options=(getEditorOptions true) code=(i18nDefaultValue) }} + {{else}} +
+
+ {{label}} +
+ {{> CodeMirror name=_id options=getEditorOptions code=value }} + {{setEditorOnBlur _id}} +
+ + +
-
+ {{/if}} {{/if}} {{#if $eq type 'action'}} diff --git a/server/lib/accounts.coffee b/server/lib/accounts.coffee index 78d9c685504..df90647b933 100644 --- a/server/lib/accounts.coffee +++ b/server/lib/accounts.coffee @@ -30,20 +30,27 @@ Accounts.emailTemplates.resetPassword.text = (user, url) -> url = url.replace /\/#\//, '/' resetPasswordText user, url -if RocketChat.settings.get 'Accounts_Enrollment_Email_Subject' - Accounts.emailTemplates.enrollAccount.subject = (user) -> +Accounts.emailTemplates.enrollAccount.subject = (user) -> + if RocketChat.settings.get 'Accounts_Enrollment_Customized' return RocketChat.settings.get 'Accounts_Enrollment_Email_Subject' + else + return TAPi18n.__('Accounts_Enrollment_Email_Subject_Default', { lng: user?.language || RocketChat.settings.get('language') || 'en' }) -if RocketChat.settings.get 'Accounts_Enrollment_Email' - Accounts.emailTemplates.enrollAccount.text = (user, url) -> - text = RocketChat.settings.get 'Accounts_Enrollment_Email' - text = text.replace /\[name\]/g, user.name or '' - text = text.replace /\[fname\]/g, _.strLeft(user.name, ' ') or '' - text = text.replace /\[lname\]/g, _.strRightBack(user.name, ' ') or '' - text = text.replace /\[email\]/g, user.emails?[0]?.address or '' - text = text.replace /\[Site_Name\]/g, RocketChat.settings.get("Site_Name") or '' - text = text.replace /\[Site_URL\]/g, RocketChat.settings.get("Site_Url") or '' - return text +Accounts.emailTemplates.enrollAccount.text = (user, url) -> + + if RocketChat.settings.get 'Accounts_Enrollment_Customized' + html = RocketChat.settings.get 'Accounts_Enrollment_Email' + else + html = TAPi18n.__('Accounts_Enrollment_Email_Default', { lng: user?.language || RocketChat.settings.get('language') || 'en' }) + + header = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Header') || "") + footer = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Footer') || "") + html = RocketChat.placeholders.replace(html, { + name: user.name, + email: user.emails?[0]?.address + }); + + return header + html + footer; Accounts.onCreateUser (options, user) -> # console.log 'onCreateUser ->',JSON.stringify arguments, null, ' '