Client's public folder renamed to static
Regular campaign sender seems to have most of the code in place. (Not tested.)
This commit is contained in:
parent
89eabea0de
commit
63765f7222
354 changed files with 836 additions and 324 deletions
|
|
@ -5,8 +5,10 @@ const config = require('config');
|
|||
const forms = require('../models/forms');
|
||||
const shares = require('../models/shares');
|
||||
const urls = require('./urls');
|
||||
const { AppType } = require('../shared/app');
|
||||
|
||||
async function getAnonymousConfig(context, trusted) {
|
||||
|
||||
async function getAnonymousConfig(context, appType) {
|
||||
return {
|
||||
authMethod: passport.authMethod,
|
||||
isAuthMethodLocal: passport.isAuthMethodLocal,
|
||||
|
|
@ -17,7 +19,9 @@ async function getAnonymousConfig(context, trusted) {
|
|||
trustedUrlBaseDir: urls.getTrustedUrlBaseDir(),
|
||||
sandboxUrlBase: urls.getSandboxUrlBase(),
|
||||
sandboxUrlBaseDir: urls.getSandboxUrlBaseDir(),
|
||||
trusted
|
||||
publicUrlBase: urls.getPublicUrlBase(),
|
||||
publicUrlBaseDir: urls.getPublicUrlBaseDir(),
|
||||
appType
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -36,8 +40,7 @@ async function getAuthenticatedConfig(context) {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getAuthenticatedConfig,
|
||||
getAnonymousConfig
|
||||
};
|
||||
|
||||
module.exports.getAuthenticatedConfig = getAuthenticatedConfig;
|
||||
module.exports.getAnonymousConfig = getAnonymousConfig;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,63 +3,11 @@
|
|||
let _ = require('./translate')._;
|
||||
|
||||
module.exports = {
|
||||
getDefaultMergeTags,
|
||||
getRSSMergeTags,
|
||||
enforce,
|
||||
cleanupFromPost,
|
||||
filterObject
|
||||
};
|
||||
|
||||
function getDefaultMergeTags() {
|
||||
return [{
|
||||
key: 'LINK_UNSUBSCRIBE',
|
||||
value: _('URL that points to the unsubscribe page')
|
||||
}, {
|
||||
key: 'LINK_PREFERENCES',
|
||||
value: _('URL that points to the preferences page of the subscriber')
|
||||
}, {
|
||||
key: 'LINK_BROWSER',
|
||||
value: _('URL to preview the message in a browser')
|
||||
}, {
|
||||
key: 'EMAIL',
|
||||
value: _('Email address')
|
||||
}, {
|
||||
key: 'SUBSCRIPTION_ID',
|
||||
value: _('Unique ID that identifies the recipient')
|
||||
}, {
|
||||
key: 'LIST_ID',
|
||||
value: _('Unique ID that identifies the list used for this campaign')
|
||||
}, {
|
||||
key: 'CAMPAIGN_ID',
|
||||
value: _('Unique ID that identifies current campaign')
|
||||
}];
|
||||
}
|
||||
|
||||
function getRSSMergeTags() {
|
||||
return [{
|
||||
key: 'RSS_ENTRY',
|
||||
value: _('content from an RSS entry')
|
||||
}, {
|
||||
key: 'RSS_ENTRY_TITLE',
|
||||
value: _('RSS entry title')
|
||||
}, {
|
||||
key: 'RSS_ENTRY_DATE',
|
||||
value: _('RSS entry date')
|
||||
}, {
|
||||
key: 'RSS_ENTRY_LINK',
|
||||
value: _('RSS entry link')
|
||||
}, {
|
||||
key: 'RSS_ENTRY_CONTENT',
|
||||
value: _('content from an RSS entry')
|
||||
}, {
|
||||
key: 'RSS_ENTRY_SUMMARY',
|
||||
value: _('RSS entry summary')
|
||||
}, {
|
||||
key: 'RSS_ENTRY_IMAGE_URL',
|
||||
value: _('RSS entry image URL')
|
||||
}];
|
||||
}
|
||||
|
||||
function enforce(condition, message) {
|
||||
if (!condition) {
|
||||
throw new Error(message);
|
||||
|
|
|
|||
|
|
@ -56,6 +56,28 @@ function invalidateMailer(sendConfigurationId) {
|
|||
|
||||
|
||||
async function _sendMail(transport, mail, template) {
|
||||
let tryCount = 0;
|
||||
const trySend = (callback) => {
|
||||
tryCount++;
|
||||
transport.sendMail(mail, (err, info) => {
|
||||
if (err) {
|
||||
log.error('Mail', err);
|
||||
if (err.responseCode && err.responseCode >= 400 && err.responseCode < 500 && tryCount <= 5) {
|
||||
// temporary error, try again
|
||||
log.verbose('Mail', 'Retrying after %s sec. ...', tryCount);
|
||||
return setTimeout(trySend, tryCount * 1000);
|
||||
}
|
||||
return callback(err);
|
||||
}
|
||||
return callback(null, info);
|
||||
});
|
||||
};
|
||||
|
||||
const trySendAsync = bluebird.promisify(trySend);
|
||||
return await trySendAsync();
|
||||
}
|
||||
|
||||
async function _sendTransactionalMail(transport, mail, template) {
|
||||
if (!mail.headers) {
|
||||
mail.headers = {};
|
||||
}
|
||||
|
|
@ -83,28 +105,9 @@ async function _sendMail(transport, mail, template) {
|
|||
});
|
||||
}
|
||||
|
||||
let tryCount = 0;
|
||||
const trySend = (callback) => {
|
||||
tryCount++;
|
||||
transport.sendMail(mail, (err, info) => {
|
||||
if (err) {
|
||||
log.error('Mail', err);
|
||||
if (err.responseCode && err.responseCode >= 400 && err.responseCode < 500 && tryCount <= 5) {
|
||||
// temporary error, try again
|
||||
log.verbose('Mail', 'Retrying after %s sec. ...', tryCount);
|
||||
return setTimeout(trySend, tryCount * 1000);
|
||||
}
|
||||
return callback(err);
|
||||
}
|
||||
return callback(null, info);
|
||||
});
|
||||
};
|
||||
|
||||
const trySendAsync = bluebird.promisify(trySend);
|
||||
return await trySendAsync();
|
||||
return await _sendMail(transport, mail);
|
||||
}
|
||||
|
||||
|
||||
async function _createTransport(sendConfiguration) {
|
||||
const mailerSettings = sendConfiguration.mailer_settings;
|
||||
const mailerType = sendConfiguration.mailer_type;
|
||||
|
|
@ -117,7 +120,7 @@ async function _createTransport(sendConfiguration) {
|
|||
existingListeners = existingTransport.listeners('idle');
|
||||
existingTransport.removeAllListeners('idle');
|
||||
existingTransport.removeAllListeners('stream');
|
||||
existingTransport.checkThrottling = null;
|
||||
existingTransport.throttleWait = null;
|
||||
}
|
||||
|
||||
const logFunc = (...args) => {
|
||||
|
|
@ -190,7 +193,7 @@ async function _createTransport(sendConfiguration) {
|
|||
existingListeners.forEach(listener => transport.on('idle', listener));
|
||||
}
|
||||
|
||||
let checkThrottling;
|
||||
let throttleWait;
|
||||
|
||||
if (mailerType === sendConfigurations.MailerType.GENERIC_SMTP || mailerType === sendConfigurations.MailerType.ZONE_MTA) {
|
||||
let throttling = mailerSettings.throttling;
|
||||
|
|
@ -200,7 +203,7 @@ async function _createTransport(sendConfiguration) {
|
|||
|
||||
let lastCheck = Date.now();
|
||||
|
||||
checkThrottling = function (next) {
|
||||
throttleWait = function (next) {
|
||||
if (!throttling) {
|
||||
return next();
|
||||
}
|
||||
|
|
@ -218,12 +221,13 @@ async function _createTransport(sendConfiguration) {
|
|||
}
|
||||
};
|
||||
} else {
|
||||
checkThrottling = next => next();
|
||||
throttleWait = next => next();
|
||||
}
|
||||
|
||||
transport.mailer = {
|
||||
checkThrottling,
|
||||
sendMail: async (mail, template) => await _sendMail(transport, mail, template)
|
||||
throttleWait: bluebird.promisify(throttleWait),
|
||||
sendTransationalMail: async (mail, template) => await _sendTransactionalMail(transport, mail, template),
|
||||
sendMassMail: async (mail, template) => await _sendMail(transport, mail)
|
||||
};
|
||||
|
||||
transports.set(sendConfiguration.id, transport);
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ async function _sendMail(list, email, template, subject, relativeUrls, subscript
|
|||
try {
|
||||
if (list.send_configuration) {
|
||||
const mailer = await mailers.getOrCreateMailer(list.send_configuration);
|
||||
await mailer.sendMail({
|
||||
await mailer.sendTransactionalMail({
|
||||
from: {
|
||||
name: configItems.defaultFrom,
|
||||
address: configItems.defaultAddress
|
||||
|
|
|
|||
48
lib/tools.js
48
lib/tools.js
|
|
@ -4,6 +4,7 @@ const _ = require('./translate')._;
|
|||
const util = require('util');
|
||||
const isemail = require('isemail');
|
||||
const path = require('path');
|
||||
const {getPublicUrl} = require('./urls');
|
||||
|
||||
const bluebird = require('bluebird');
|
||||
|
||||
|
|
@ -11,11 +12,13 @@ const hasher = require('node-object-hash')();
|
|||
const mjml = require('mjml');
|
||||
const hbs = require('hbs');
|
||||
const juice = require('juice');
|
||||
let he = require('he');
|
||||
|
||||
const fsReadFile = bluebird.promisify(require('fs').readFile);
|
||||
const jsdomEnv = bluebird.promisify(require('jsdom').env);
|
||||
|
||||
|
||||
|
||||
const templates = new Map();
|
||||
|
||||
async function getTemplate(template) {
|
||||
|
|
@ -102,6 +105,38 @@ function validateEmailGetMessage(result, address) {
|
|||
}
|
||||
}
|
||||
|
||||
function formatMessage(campaign, list, subscription, mergeTags, message, filter, isHTML) {
|
||||
filter = typeof filter === 'function' ? filter : (str => str);
|
||||
|
||||
let links = getMessageLinks(campaign, list, subscription);
|
||||
|
||||
let getValue = key => {
|
||||
key = (key || '').toString().toUpperCase().trim();
|
||||
if (links.hasOwnProperty(key)) {
|
||||
return links[key];
|
||||
}
|
||||
if (mergeTags.hasOwnProperty(key)) {
|
||||
let value = (mergeTags[key] || '').toString();
|
||||
let containsHTML = /<[a-z][\s\S]*>/.test(value);
|
||||
return isHTML ? he.encode((containsHTML ? value : value.replace(/(?:\r\n|\r|\n)/g, '<br/>')), {
|
||||
useNamedReferences: true,
|
||||
allowUnsafeSymbols: true
|
||||
}) : (containsHTML ? htmlToText.fromString(value) : value);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
return message.replace(/\[([a-z0-9_]+)(?:\/([^\]]+))?\]/ig, (match, identifier, fallback) => {
|
||||
identifier = identifier.toUpperCase();
|
||||
let value = getValue(identifier);
|
||||
if (value === false) {
|
||||
return match;
|
||||
}
|
||||
value = (value || fallback || '').trim();
|
||||
return filter(value);
|
||||
});
|
||||
}
|
||||
|
||||
async function prepareHtml(html) {
|
||||
if (!(html || '').toString().trim()) {
|
||||
return false;
|
||||
|
|
@ -137,12 +172,23 @@ async function prepareHtml(html) {
|
|||
return juice(preparedHtml);
|
||||
}
|
||||
|
||||
function getMessageLinks(campaign, list, subscription) {
|
||||
return {
|
||||
LINK_UNSUBSCRIBE: getPublicUrl('/subscription/' + list.cid + '/unsubscribe/' + subscription.cid + '?c=' + campaign.cid),
|
||||
LINK_PREFERENCES: getPublicUrl('/subscription/' + list.cid + '/manage/' + subscription.cid),
|
||||
LINK_BROWSER: getPublicUrl('/archive/' + campaign.cid + '/' + list.cid + '/' + subscription.cid),
|
||||
CAMPAIGN_ID: campaign.cid,
|
||||
LIST_ID: list.cid,
|
||||
SUBSCRIPTION_ID: subscription.cid
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
validateEmail,
|
||||
validateEmailGetMessage,
|
||||
mergeTemplateIntoLayout,
|
||||
getTemplate,
|
||||
prepareHtml
|
||||
prepareHtml,
|
||||
getMessageLinks
|
||||
};
|
||||
|
||||
|
|
|
|||
27
lib/urls.js
27
lib/urls.js
|
|
@ -12,6 +12,10 @@ function getSandboxUrlBase() {
|
|||
return urllib.resolve(config.www.sandboxUrlBase, '');
|
||||
}
|
||||
|
||||
function getPublicUrlBase() {
|
||||
return urllib.resolve(config.www.publicUrlBase, '');
|
||||
}
|
||||
|
||||
function getTrustedUrl(path) {
|
||||
return urllib.resolve(config.www.trustedUrlBase, path || '');
|
||||
}
|
||||
|
|
@ -24,21 +28,34 @@ function getSandboxUrl(path, context) {
|
|||
}
|
||||
}
|
||||
|
||||
function getPublicUrl(path) {
|
||||
return urllib.resolve(config.www.publicUrlBase, path || '');
|
||||
}
|
||||
|
||||
|
||||
function getTrustedUrlBaseDir() {
|
||||
const ivisUrl = urllib.parse(config.www.trustedUrlBase);
|
||||
return ivisUrl.pathname;
|
||||
const mailtrainUrl = urllib.parse(config.www.trustedUrlBase);
|
||||
return mailtrainUrl.pathname;
|
||||
}
|
||||
|
||||
function getSandboxUrlBaseDir() {
|
||||
const ivisUrl = urllib.parse(config.www.sandboxUrlBase);
|
||||
return ivisUrl.pathname;
|
||||
const mailtrainUrl = urllib.parse(config.www.sandboxUrlBase);
|
||||
return mailtrainUrl.pathname;
|
||||
}
|
||||
|
||||
function getPublicUrlBaseDir() {
|
||||
const mailtrainUrl = urllib.parse(config.www.publicUrlBase);
|
||||
return mailtrainUrl.pathname;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getTrustedUrl,
|
||||
getSandboxUrl,
|
||||
getPublicUrl,
|
||||
getTrustedUrlBase,
|
||||
getSandboxUrlBase,
|
||||
getPublicUrlBase,
|
||||
getTrustedUrlBaseDir,
|
||||
getSandboxUrlBaseDir
|
||||
getSandboxUrlBaseDir,
|
||||
getPublicUrlBaseDir
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue