diff --git a/lib/add-trackers.js b/lib/add-trackers.js index ebd987db..edbf37fb 100644 --- a/lib/add-trackers.js +++ b/lib/add-trackers.js @@ -98,15 +98,19 @@ async function addClickTrackers(html, account, messageId, baseUrl) { return html; } -async function addTrackers(raw, account, messageId, baseUrl) { +async function addTrackers(raw, account, messageId, baseUrl, opts) { + let { trackClicks, trackOpens } = opts || {}; let openTrackerAdded = false; return await rewriteTextNodes(raw, { async htmlRewriter(html) { - if (!openTrackerAdded) { + if (trackOpens && !openTrackerAdded) { html = await addOpenTracker(html, account, messageId, baseUrl); openTrackerAdded = true; } - html = await addClickTrackers(html, account, messageId, baseUrl); + + if (trackClicks) { + html = await addClickTrackers(html, account, messageId, baseUrl); + } return html; } }); diff --git a/lib/api-client/base-client.js b/lib/api-client/base-client.js index 0d029aad..f4c5d5bf 100644 --- a/lib/api-client/base-client.js +++ b/lib/api-client/base-client.js @@ -1230,7 +1230,10 @@ class BaseClient { } } - let { raw, hasBcc, envelope, subject, messageId, sendAt, deliveryAttempts, trackingEnabled, gateway } = await getRawEmail(data, licenseInfo); + let { raw, hasBcc, envelope, subject, messageId, sendAt, deliveryAttempts, trackClicks, trackOpens, trackingEnabled, gateway } = await getRawEmail( + data, + licenseInfo + ); if (data.dryRun) { let response = { @@ -1277,9 +1280,26 @@ class BaseClient { trackingEnabled = (await settings.get('trackSentMessages')) || false; } - if (raw && trackingEnabled && baseUrl) { + if (typeof trackClicks !== 'boolean') { + trackClicks = await settings.get('trackClicks'); + if (typeof trackClicks !== 'boolean') { + trackClicks = trackingEnabled; + } + } + + if (typeof trackOpens !== 'boolean') { + trackOpens = await settings.get('trackOpens'); + if (typeof trackOpens !== 'boolean') { + trackOpens = trackingEnabled; + } + } + + if (raw && (trackClicks || trackOpens) && baseUrl) { // add open and click tracking - raw = await addTrackers(raw, accountData.account, messageId, baseUrl); + raw = await addTrackers(raw, accountData.account, messageId, baseUrl, { + trackClicks, + trackOpens + }); } let now = new Date(); diff --git a/lib/get-raw-email.js b/lib/get-raw-email.js index df505d8b..26a9cdc0 100644 --- a/lib/get-raw-email.js +++ b/lib/get-raw-email.js @@ -31,6 +31,7 @@ async function processMessage(data, licenseInfo) { let deliveryAttempts = data.deliveryAttempts; let subject = data.subject; let trackingEnabled = data.trackingEnabled; + let gateway = data.gateway; const splitter = new Splitter(); @@ -263,7 +264,22 @@ async function processMessage(data, licenseInfo) { splitter.end(raw); }); - return { raw: message, hasBcc, messageId, envelope, subject, sendAt, deliveryAttempts, trackingEnabled, gateway }; + let trackClicks = typeof data.trackClicks === 'boolean' ? data.trackClicks : trackingEnabled; + let trackOpens = typeof data.trackOpens === 'boolean' ? data.trackClicks : trackingEnabled; + + return { + raw: message, + hasBcc, + messageId, + envelope, + subject, + sendAt, + deliveryAttempts, + trackClicks, + trackOpens, + trackingEnabled, + gateway + }; } async function removeBcc(raw) { @@ -349,6 +365,9 @@ async function getRawEmail(data, licenseInfo) { let gateway = data.gateway; let trackingEnabled = data.trackingEnabled; + let trackClicks = typeof data.trackClicks === 'boolean' ? data.trackClicks : trackingEnabled; + let trackOpens = typeof data.trackOpens === 'boolean' ? data.trackOpens : trackingEnabled; + let html = data.html || null; if (data.previewText && html) { let previewText = data.previewText; @@ -385,6 +404,8 @@ async function getRawEmail(data, licenseInfo) { deliveryAttempts: null, gateway: null, trackingEnabled: null, + trackClicks: null, + trackOpens: null, previewText: null } ) @@ -408,6 +429,8 @@ async function getRawEmail(data, licenseInfo) { sendAt, deliveryAttempts, trackingEnabled, + trackClicks, + trackOpens, gateway }; } diff --git a/lib/routes-ui.js b/lib/routes-ui.js index 1dab0f0e..ed41d3d0 100644 --- a/lib/routes-ui.js +++ b/lib/routes-ui.js @@ -1223,6 +1223,8 @@ function applyRoutes(server, call) { method: 'GET', path: '/admin/config/service', async handler(request, h) { + let trackSentMessages = (await settings.get('trackSentMessages')) || false; + const values = { serviceUrl: (await settings.get('serviceUrl')) || null, serviceSecret: (await settings.get('serviceSecret')) || null, @@ -1232,7 +1234,10 @@ function applyRoutes(server, call) { scriptEnv: (await settings.get('scriptEnv')) || '', enableTokens: !(await settings.get('disableTokens')), enableApiProxy: (await settings.get('enableApiProxy')) || false, - trackSentMessages: (await settings.get('trackSentMessages')) || false, + + trackClicks: await settings.get('trackClicks'), + trackOpens: await settings.get('trackOpens'), + resolveGmailCategories: (await settings.get('resolveGmailCategories')) || false, enableOAuthTokensApi: (await settings.get('enableOAuthTokensApi')) || false, @@ -1242,6 +1247,14 @@ function applyRoutes(server, call) { timezone: (await settings.get('timezone')) || false }; + if (typeof values.trackClicks !== 'boolean') { + values.trackClicks = trackSentMessages; + } + + if (typeof values.trackOpens !== 'boolean') { + values.trackOpens = trackSentMessages; + } + if (typeof values.deliveryAttempts !== 'number') { values.deliveryAttempts = DEFAULT_DELIVERY_ATTEMPTS; } @@ -1281,7 +1294,8 @@ function applyRoutes(server, call) { scriptEnv: request.payload.scriptEnv, disableTokens: !request.payload.enableTokens, enableApiProxy: request.payload.enableApiProxy, - trackSentMessages: request.payload.trackSentMessages, + trackOpens: request.payload.trackOpens, + trackClicks: request.payload.trackClicks, resolveGmailCategories: request.payload.resolveGmailCategories, enableOAuthTokensApi: request.payload.enableOAuthTokensApi, ignoreMailCertErrors: request.payload.ignoreMailCertErrors, @@ -1380,7 +1394,8 @@ function applyRoutes(server, call) { templateHeader: settingsSchema.templateHeader.default(''), scriptEnv: settingsSchema.scriptEnv.default(''), enableApiProxy: settingsSchema.enableApiProxy.default(false), - trackSentMessages: settingsSchema.trackSentMessages.default(false), + trackOpens: settingsSchema.trackOpens.default(false), + trackClicks: settingsSchema.trackClicks.default(false), resolveGmailCategories: settingsSchema.resolveGmailCategories.default(false), ignoreMailCertErrors: settingsSchema.ignoreMailCertErrors.default(false), diff --git a/lib/schemas.js b/lib/schemas.js index 8877162f..34c8a16c 100644 --- a/lib/schemas.js +++ b/lib/schemas.js @@ -74,7 +74,18 @@ const settingsSchema = { trackSentMessages: Joi.boolean() .truthy('Y', 'true', '1', 'on') .falsy('N', 'false', 0, '') - .description('If true, then rewrite html links in sent emails to track opens and clicks'), + .description('If true, then rewrite html links in sent emails to track opens and clicks') + .meta({ swaggerHidden: true }), + + trackClicks: Joi.boolean() + .truthy('Y', 'true', '1', 'on') + .falsy('N', 'false', 0, '') + .description('If true, then rewrite html links in sent emails to track clicks'), + + trackOpens: Joi.boolean() + .truthy('Y', 'true', '1', 'on') + .falsy('N', 'false', 0, '') + .description('If true, then add an open tracking beacon image to email html'), resolveGmailCategories: Joi.boolean() .truthy('Y', 'true', '1', 'on') diff --git a/views/config/service.hbs b/views/config/service.hbs index e7bc68b3..732d0beb 100644 --- a/views/config/service.hbs +++ b/views/config/service.hbs @@ -100,7 +100,7 @@ - +
{{errors.serviceSecret}} {{/if}} - HMAC secret for signing public requests. Changing this value - invalidates all tracking links in emails that were sent with the tracking option enabled. + HMAC secret used for signing public requests. Changing this value + will invalidate all tracking links in emails sent with tracking enabled.
@@ -134,9 +134,9 @@ {{#if errors.enableTokens}} {{errors.enableTokens}} {{/if}} - You can disable access tokens when in development. Not - requiring access tokens to authorize API requests makes running API requests from the - command line easier. Always enable access tokens in production. + Disable this option during development to simplify running API + requests from the command line. However, it is recommended to always enable access tokens in + production to ensure secure API request authorization.
@@ -147,18 +147,17 @@ -
@@ -166,18 +165,16 @@ - + {{#if errors.enableOAuthTokensApi}} {{errors.enableOAuthTokensApi}} {{/if}} - If you want to re-use access tokens managed by EmailEngine in other - parts of your app, you can enable the Enable this option if you need to reuse OAuth2 access tokens managed + by EmailEngine in other parts of your application. Note that oauth-token API - endpoint. Access to OAuth2 tokens is disabled by default due to the security risk of - potentially leaking these tokens. + rel="noopener noreferrer">access to OAuth2 tokens is disabled by default due to the + potential security risks of leaking these tokens.
@@ -188,8 +185,7 @@ -