mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-24 06:35:49 +03:00
Added the feedback buttons in the emails (#15619)
closes TryGhost/Team#2046 closes TryGhost/Team#2045 - Added feedback buttons markup. - Added feedback links generation.
This commit is contained in:
parent
e074676e12
commit
e831be6bc2
@ -1,7 +1,27 @@
|
|||||||
class AudienceFeedbackService {
|
class AudienceFeedbackService {
|
||||||
buildLink() {
|
/** @type URL */
|
||||||
// todo
|
#baseURL;
|
||||||
return new URL('https://example.com');
|
/**
|
||||||
|
* @param {object} deps
|
||||||
|
* @param {object} deps.config
|
||||||
|
* @param {URL} deps.config.baseURL
|
||||||
|
*/
|
||||||
|
constructor(deps) {
|
||||||
|
this.#baseURL = deps.config.baseURL;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {string} uuid
|
||||||
|
* @param {string} postId
|
||||||
|
* @param {0 | 1} score
|
||||||
|
*/
|
||||||
|
buildLink(uuid, postId, score) {
|
||||||
|
const url = new URL(this.#baseURL);
|
||||||
|
url.searchParams.set('action', 'feedback');
|
||||||
|
url.searchParams.set('post', postId);
|
||||||
|
url.searchParams.set('uuid', uuid);
|
||||||
|
url.searchParams.set('score', `${score}`);
|
||||||
|
|
||||||
|
return url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
const urlUtils = require('../../../shared/url-utils');
|
||||||
const FeedbackRepository = require('./FeedbackRepository');
|
const FeedbackRepository = require('./FeedbackRepository');
|
||||||
|
|
||||||
class AudienceFeedbackServiceWrapper {
|
class AudienceFeedbackServiceWrapper {
|
||||||
@ -20,7 +21,11 @@ class AudienceFeedbackServiceWrapper {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Expose the service
|
// Expose the service
|
||||||
this.service = new AudienceFeedbackService();
|
this.service = new AudienceFeedbackService({
|
||||||
|
config: {
|
||||||
|
baseURL: new URL(urlUtils.urlFor('home', true))
|
||||||
|
}
|
||||||
|
});
|
||||||
this.controller = new AudienceFeedbackController({repository: this.repository});
|
this.controller = new AudienceFeedbackController({repository: this.repository});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ const debug = require('@tryghost/debug')('mega');
|
|||||||
const postEmailSerializer = require('../mega/post-email-serializer');
|
const postEmailSerializer = require('../mega/post-email-serializer');
|
||||||
const configService = require('../../../shared/config');
|
const configService = require('../../../shared/config');
|
||||||
const settingsCache = require('../../../shared/settings-cache');
|
const settingsCache = require('../../../shared/settings-cache');
|
||||||
|
const labs = require('../../../shared/labs');
|
||||||
|
|
||||||
const messages = {
|
const messages = {
|
||||||
error: 'The email service received an error from mailgun and was unable to send.'
|
error: 'The email service received an error from mailgun and was unable to send.'
|
||||||
@ -234,6 +235,11 @@ module.exports = {
|
|||||||
unsubscribe_url: postEmailSerializer.createUnsubscribeUrl(recipient.member_uuid, {newsletterUuid})
|
unsubscribe_url: postEmailSerializer.createUnsubscribeUrl(recipient.member_uuid, {newsletterUuid})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (labs.isSet('audienceFeedback')) {
|
||||||
|
// create unique urls for every recipient (for example, for feedback buttons)
|
||||||
|
emailData = postEmailSerializer.createUserLinks(emailData, recipient.member_uuid);
|
||||||
|
}
|
||||||
|
|
||||||
// computed properties on recipients - TODO: better way of handling these
|
// computed properties on recipients - TODO: better way of handling these
|
||||||
recipient.member_first_name = (recipient.member_name || '').split(' ')[0];
|
recipient.member_first_name = (recipient.member_name || '').split(' ')[0];
|
||||||
|
|
||||||
|
69
ghost/core/core/server/services/mega/feedback-buttons.js
Normal file
69
ghost/core/core/server/services/mega/feedback-buttons.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
const {Color} = require('@tryghost/color-utils');
|
||||||
|
const audienceFeedback = require('../audience-feedback');
|
||||||
|
|
||||||
|
const templateStrings = {
|
||||||
|
like: '%{feedback_button_like}%',
|
||||||
|
dislike: '%{feedback_button_dislike}%'
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateLinks = (postId, uuid, html) => {
|
||||||
|
const positiveLink = audienceFeedback.service.buildLink(
|
||||||
|
uuid,
|
||||||
|
postId,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
const negativeLink = audienceFeedback.service.buildLink(
|
||||||
|
uuid,
|
||||||
|
postId,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
html = html.replace(templateStrings.like, positiveLink.href);
|
||||||
|
html = html.replace(templateStrings.dislike, negativeLink.href);
|
||||||
|
|
||||||
|
return html;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTemplate = (accentColor) => {
|
||||||
|
const likeButtonHtml = getButtonHtml(templateStrings.like, 'More like this', accentColor);
|
||||||
|
const dislikeButtonHtml = getButtonHtml(templateStrings.dislike, 'Less like this', accentColor);
|
||||||
|
|
||||||
|
return (`
|
||||||
|
<tr>
|
||||||
|
<td dir="ltr" width="100%" style="background-color: #ffffff; text-align: center; padding: 40px 4px; border-bottom: 1px solid #e5eff5" align="center">
|
||||||
|
<h3 style="text-align: center; margin-bottom: 22px; font-size: 17px; letter-spacing: -0.2px; margin-top: 0 !important;">What did you think of this post?</h3>
|
||||||
|
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="margin: auto; width: auto !important;">
|
||||||
|
<tr>
|
||||||
|
${likeButtonHtml}
|
||||||
|
${dislikeButtonHtml}
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`);
|
||||||
|
};
|
||||||
|
|
||||||
|
function getButtonHtml(href, buttonText, accentColor) {
|
||||||
|
const color = new Color(accentColor);
|
||||||
|
const bgColor = `${accentColor}10`;
|
||||||
|
const textColor = color.darken(0.6).hex();
|
||||||
|
|
||||||
|
return (`
|
||||||
|
<td dir="ltr" valign="top" align="center" style="font-family: inherit; font-size: 14px; text-align: center;" nowrap>
|
||||||
|
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="width: auto !important;">
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 0 6px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';">
|
||||||
|
<a href=${href} style="background-color: ${bgColor}; color: ${textColor}; border-radius: 22px; font-family: inherit; padding: 12px 20px; border: none; font-size: 14px; font-weight: bold; line-height: 100%; text-decoration: none; display: block;">
|
||||||
|
${buttonText}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
generateLinks,
|
||||||
|
getTemplate
|
||||||
|
};
|
@ -16,11 +16,12 @@ const urlService = require('../../services/url');
|
|||||||
const linkReplacer = require('@tryghost/link-replacer');
|
const linkReplacer = require('@tryghost/link-replacer');
|
||||||
const linkTracking = require('../link-tracking');
|
const linkTracking = require('../link-tracking');
|
||||||
const memberAttribution = require('../member-attribution');
|
const memberAttribution = require('../member-attribution');
|
||||||
|
const feedbackButtons = require('./feedback-buttons');
|
||||||
|
|
||||||
const ALLOWED_REPLACEMENTS = ['first_name', 'uuid'];
|
const ALLOWED_REPLACEMENTS = ['first_name', 'uuid'];
|
||||||
|
|
||||||
const PostEmailSerializer = {
|
const PostEmailSerializer = {
|
||||||
|
|
||||||
// Format a full html document ready for email by inlining CSS, adjusting links,
|
// Format a full html document ready for email by inlining CSS, adjusting links,
|
||||||
// and performing any client-specific fixes
|
// and performing any client-specific fixes
|
||||||
formatHtmlForEmail(html) {
|
formatHtmlForEmail(html) {
|
||||||
@ -107,6 +108,23 @@ const PostEmailSerializer = {
|
|||||||
return signupUrl.href;
|
return signupUrl.href;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* createUserLinks
|
||||||
|
*
|
||||||
|
* Generate personalised links for each user
|
||||||
|
*
|
||||||
|
* @param {string} memberUuid member uuid
|
||||||
|
* @param {Object} email
|
||||||
|
*/
|
||||||
|
createUserLinks(email, memberUuid) {
|
||||||
|
const result = {...email};
|
||||||
|
|
||||||
|
result.html = feedbackButtons.generateLinks(result.post.id, memberUuid, result.html);
|
||||||
|
result.plaintext = htmlToPlaintext.email(result.html);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
// NOTE: serialization is needed to make sure we do post transformations such as image URL transformation from relative to absolute
|
// NOTE: serialization is needed to make sure we do post transformations such as image URL transformation from relative to absolute
|
||||||
async serializePostModel(model) {
|
async serializePostModel(model) {
|
||||||
// fetch mobiledoc rather than html and plaintext so we can render email-specific contents
|
// fetch mobiledoc rather than html and plaintext so we can render email-specific contents
|
||||||
@ -206,6 +224,7 @@ const PostEmailSerializer = {
|
|||||||
titleAlignment: newsletter.get('title_alignment'),
|
titleAlignment: newsletter.get('title_alignment'),
|
||||||
bodyFontCategory: newsletter.get('body_font_category'),
|
bodyFontCategory: newsletter.get('body_font_category'),
|
||||||
showBadge: newsletter.get('show_badge'),
|
showBadge: newsletter.get('show_badge'),
|
||||||
|
feedbackEnabled: newsletter.get('feedback_enabled'),
|
||||||
footerContent: newsletter.get('footer_content'),
|
footerContent: newsletter.get('footer_content'),
|
||||||
showHeaderName: newsletter.get('show_header_name'),
|
showHeaderName: newsletter.get('show_header_name'),
|
||||||
accentColor,
|
accentColor,
|
||||||
@ -335,7 +354,7 @@ const PostEmailSerializer = {
|
|||||||
plaintext: post.plaintext
|
plaintext: post.plaintext
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a part of the email is members-only and the post is paid-only, add a paywall:
|
* If a part of the email is members-only and the post is paid-only, add a paywall:
|
||||||
* - Just before sending the email, we'll hide the paywall or paid content depending on the member segment it is sent to.
|
* - Just before sending the email, we'll hide the paywall or paid content depending on the member segment it is sent to.
|
||||||
* - We already need to do URL-replacement on the HTML here
|
* - We already need to do URL-replacement on the HTML here
|
||||||
@ -369,7 +388,7 @@ const PostEmailSerializer = {
|
|||||||
|
|
||||||
// Add link click tracking
|
// Add link click tracking
|
||||||
url = await linkTracking.service.addTrackingToUrl(url, post, '--uuid--');
|
url = await linkTracking.service.addTrackingToUrl(url, post, '--uuid--');
|
||||||
|
|
||||||
// We need to convert to a string at this point, because we need invalid string characters in the URL
|
// We need to convert to a string at this point, because we need invalid string characters in the URL
|
||||||
const str = url.toString().replace(/--uuid--/g, '%%{uuid}%%');
|
const str = url.toString().replace(/--uuid--/g, '%%{uuid}%%');
|
||||||
return str;
|
return str;
|
||||||
@ -490,7 +509,7 @@ const PostEmailSerializer = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
result.html = this.formatHtmlForEmail($.html());
|
result.html = this.formatHtmlForEmail($.html());
|
||||||
result.plaintext = htmlToPlaintext.email(result.html);
|
result.plaintext = htmlToPlaintext.email(result.html);
|
||||||
delete result.post;
|
delete result.post;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -501,6 +520,7 @@ module.exports = {
|
|||||||
serialize: PostEmailSerializer.serialize.bind(PostEmailSerializer),
|
serialize: PostEmailSerializer.serialize.bind(PostEmailSerializer),
|
||||||
createUnsubscribeUrl: PostEmailSerializer.createUnsubscribeUrl.bind(PostEmailSerializer),
|
createUnsubscribeUrl: PostEmailSerializer.createUnsubscribeUrl.bind(PostEmailSerializer),
|
||||||
createPostSignupUrl: PostEmailSerializer.createPostSignupUrl.bind(PostEmailSerializer),
|
createPostSignupUrl: PostEmailSerializer.createPostSignupUrl.bind(PostEmailSerializer),
|
||||||
|
createUserLinks: PostEmailSerializer.createUserLinks.bind(PostEmailSerializer),
|
||||||
renderEmailForSegment: PostEmailSerializer.renderEmailForSegment.bind(PostEmailSerializer),
|
renderEmailForSegment: PostEmailSerializer.renderEmailForSegment.bind(PostEmailSerializer),
|
||||||
parseReplacements: PostEmailSerializer.parseReplacements.bind(PostEmailSerializer),
|
parseReplacements: PostEmailSerializer.parseReplacements.bind(PostEmailSerializer),
|
||||||
// Export for tests
|
// Export for tests
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const {escapeHtml: escape} = require('@tryghost/string');
|
const {escapeHtml: escape} = require('@tryghost/string');
|
||||||
|
const feedbackButtons = require('./feedback-buttons');
|
||||||
|
|
||||||
/* eslint indent: warn, no-irregular-whitespace: warn */
|
/* eslint indent: warn, no-irregular-whitespace: warn */
|
||||||
const iff = (cond, yes, no) => (cond ? yes : no);
|
const iff = (cond, yes, no) => (cond ? yes : no);
|
||||||
@ -1265,6 +1266,8 @@ ${ templateSettings.showBadge ? `
|
|||||||
|
|
||||||
<!-- END MAIN CONTENT AREA -->
|
<!-- END MAIN CONTENT AREA -->
|
||||||
|
|
||||||
|
${iff(templateSettings.feedbackEnabled, feedbackButtons.getTemplate(templateSettings.accentColor), '')}
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class="wrapper" align="center">
|
<td class="wrapper" align="center">
|
||||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="padding-top: 40px; padding-bottom: 30px;">
|
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="padding-top: 40px; padding-bottom: 30px;">
|
||||||
|
@ -416,6 +416,8 @@ table.body figcaption a {
|
|||||||
|
|
||||||
<!-- END MAIN CONTENT AREA -->
|
<!-- END MAIN CONTENT AREA -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class=\\"wrapper\\" align=\\"center\\" style=\\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; vertical-align: top; color: #15212A; box-sizing: border-box; padding: 0 20px;\\" valign=\\"top\\">
|
<td class=\\"wrapper\\" align=\\"center\\" style=\\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; vertical-align: top; color: #15212A; box-sizing: border-box; padding: 0 20px;\\" valign=\\"top\\">
|
||||||
<table role=\\"presentation\\" border=\\"0\\" cellpadding=\\"0\\" cellspacing=\\"0\\" width=\\"100%\\" style=\\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; padding-top: 40px; padding-bottom: 30px;\\">
|
<table role=\\"presentation\\" border=\\"0\\" cellpadding=\\"0\\" cellspacing=\\"0\\" width=\\"100%\\" style=\\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; padding-top: 40px; padding-bottom: 30px;\\">
|
||||||
@ -468,7 +470,7 @@ exports[`Email Preview API Read can read post email preview with email card and
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"access-control-allow-origin": "http://127.0.0.1:2369",
|
||||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "18188",
|
"content-length": "18216",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
"vary": "Accept-Version, Origin, Accept-Encoding",
|
"vary": "Accept-Version, Origin, Accept-Encoding",
|
||||||
@ -806,6 +808,8 @@ table.body figcaption a {
|
|||||||
|
|
||||||
<!-- END MAIN CONTENT AREA -->
|
<!-- END MAIN CONTENT AREA -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class=\\"wrapper\\" align=\\"center\\" style=\\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; vertical-align: top; color: #15212A; box-sizing: border-box; padding: 0 20px;\\" valign=\\"top\\">
|
<td class=\\"wrapper\\" align=\\"center\\" style=\\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; vertical-align: top; color: #15212A; box-sizing: border-box; padding: 0 20px;\\" valign=\\"top\\">
|
||||||
<table role=\\"presentation\\" border=\\"0\\" cellpadding=\\"0\\" cellspacing=\\"0\\" width=\\"100%\\" style=\\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; padding-top: 40px; padding-bottom: 30px;\\">
|
<table role=\\"presentation\\" border=\\"0\\" cellpadding=\\"0\\" cellspacing=\\"0\\" width=\\"100%\\" style=\\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; padding-top: 40px; padding-bottom: 30px;\\">
|
||||||
@ -870,7 +874,7 @@ exports[`Email Preview API Read can read post email preview with fields 2: [head
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"access-control-allow-origin": "http://127.0.0.1:2369",
|
||||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "23013",
|
"content-length": "23041",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
"vary": "Accept-Version, Origin, Accept-Encoding",
|
"vary": "Accept-Version, Origin, Accept-Encoding",
|
||||||
@ -1234,6 +1238,8 @@ table.body figcaption a {
|
|||||||
|
|
||||||
<!-- END MAIN CONTENT AREA -->
|
<!-- END MAIN CONTENT AREA -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class=\\"wrapper\\" align=\\"center\\" style=\\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; vertical-align: top; color: #15212A; box-sizing: border-box; padding: 0 20px;\\" valign=\\"top\\">
|
<td class=\\"wrapper\\" align=\\"center\\" style=\\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; vertical-align: top; color: #15212A; box-sizing: border-box; padding: 0 20px;\\" valign=\\"top\\">
|
||||||
<table role=\\"presentation\\" border=\\"0\\" cellpadding=\\"0\\" cellspacing=\\"0\\" width=\\"100%\\" style=\\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; padding-top: 40px; padding-bottom: 30px;\\">
|
<table role=\\"presentation\\" border=\\"0\\" cellpadding=\\"0\\" cellspacing=\\"0\\" width=\\"100%\\" style=\\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; padding-top: 40px; padding-bottom: 30px;\\">
|
||||||
@ -1280,7 +1286,7 @@ exports[`Email Preview API Read has custom content transformations for email com
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"access-control-allow-origin": "http://127.0.0.1:2369",
|
||||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "17950",
|
"content-length": "17978",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
"vary": "Accept-Version, Origin, Accept-Encoding",
|
"vary": "Accept-Version, Origin, Accept-Encoding",
|
||||||
@ -1618,6 +1624,8 @@ table.body figcaption a {
|
|||||||
|
|
||||||
<!-- END MAIN CONTENT AREA -->
|
<!-- END MAIN CONTENT AREA -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class=\\"wrapper\\" align=\\"center\\" style=\\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; vertical-align: top; color: #15212A; box-sizing: border-box; padding: 0 20px;\\" valign=\\"top\\">
|
<td class=\\"wrapper\\" align=\\"center\\" style=\\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; vertical-align: top; color: #15212A; box-sizing: border-box; padding: 0 20px;\\" valign=\\"top\\">
|
||||||
<table role=\\"presentation\\" border=\\"0\\" cellpadding=\\"0\\" cellspacing=\\"0\\" width=\\"100%\\" style=\\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; padding-top: 40px; padding-bottom: 30px;\\">
|
<table role=\\"presentation\\" border=\\"0\\" cellpadding=\\"0\\" cellspacing=\\"0\\" width=\\"100%\\" style=\\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; padding-top: 40px; padding-bottom: 30px;\\">
|
||||||
@ -1664,7 +1672,7 @@ exports[`Email Preview API Read uses the newsletter provided through ?newsletter
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"access-control-allow-origin": "http://127.0.0.1:2369",
|
||||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "18316",
|
"content-length": "18344",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
"vary": "Accept-Version, Origin, Accept-Encoding",
|
"vary": "Accept-Version, Origin, Accept-Encoding",
|
||||||
@ -2388,6 +2396,8 @@ table.body figcaption a {
|
|||||||
|
|
||||||
<!-- END MAIN CONTENT AREA -->
|
<!-- END MAIN CONTENT AREA -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class=\\"wrapper\\" align=\\"center\\" style=\\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; vertical-align: top; color: #15212A; box-sizing: border-box; padding: 0 20px;\\" valign=\\"top\\">
|
<td class=\\"wrapper\\" align=\\"center\\" style=\\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; vertical-align: top; color: #15212A; box-sizing: border-box; padding: 0 20px;\\" valign=\\"top\\">
|
||||||
<table role=\\"presentation\\" border=\\"0\\" cellpadding=\\"0\\" cellspacing=\\"0\\" width=\\"100%\\" style=\\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; padding-top: 40px; padding-bottom: 30px;\\">
|
<table role=\\"presentation\\" border=\\"0\\" cellpadding=\\"0\\" cellspacing=\\"0\\" width=\\"100%\\" style=\\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; padding-top: 40px; padding-bottom: 30px;\\">
|
||||||
@ -2434,7 +2444,7 @@ exports[`Email Preview API Read uses the posts newsletter by default 2: [headers
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"access-control-allow-origin": "http://127.0.0.1:2369",
|
||||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "18316",
|
"content-length": "18344",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
"vary": "Accept-Version, Origin, Accept-Encoding",
|
"vary": "Accept-Version, Origin, Accept-Encoding",
|
||||||
|
@ -7,7 +7,7 @@ const urlService = require('../../../../../core/server/services/url');
|
|||||||
const labs = require('../../../../../core/shared/labs');
|
const labs = require('../../../../../core/shared/labs');
|
||||||
const {parseReplacements, renderEmailForSegment, serialize, _getTemplateSettings, createUnsubscribeUrl, createPostSignupUrl, _PostEmailSerializer} = require('../../../../../core/server/services/mega/post-email-serializer');
|
const {parseReplacements, renderEmailForSegment, serialize, _getTemplateSettings, createUnsubscribeUrl, createPostSignupUrl, _PostEmailSerializer} = require('../../../../../core/server/services/mega/post-email-serializer');
|
||||||
const {HtmlValidate} = require('html-validate');
|
const {HtmlValidate} = require('html-validate');
|
||||||
|
|
||||||
function assertKeys(object, keys) {
|
function assertKeys(object, keys) {
|
||||||
assert.deepStrictEqual(Object.keys(object).sort(), keys.sort());
|
assert.deepStrictEqual(Object.keys(object).sort(), keys.sort());
|
||||||
}
|
}
|
||||||
@ -16,7 +16,7 @@ describe('Post Email Serializer', function () {
|
|||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
sinon.restore();
|
sinon.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates replacement pattern for valid format and value', function () {
|
it('creates replacement pattern for valid format and value', function () {
|
||||||
const html = '<html>Hey %%{first_name}%%, what is up?</html>';
|
const html = '<html>Hey %%{first_name}%%, what is up?</html>';
|
||||||
const plaintext = 'Hey %%{first_name}%%, what is up?';
|
const plaintext = 'Hey %%{first_name}%%, what is up?';
|
||||||
@ -137,7 +137,7 @@ describe('Post Email Serializer', function () {
|
|||||||
|
|
||||||
// Improve debugging and show a snippet of the invalid HTML instead of just the line number or a huge HTML-dump
|
// Improve debugging and show a snippet of the invalid HTML instead of just the line number or a huge HTML-dump
|
||||||
const parsedErrors = [];
|
const parsedErrors = [];
|
||||||
|
|
||||||
if (!report.valid) {
|
if (!report.valid) {
|
||||||
const lines = output.html.split('\n');
|
const lines = output.html.split('\n');
|
||||||
const messages = report.results[0].messages;
|
const messages = report.results[0].messages;
|
||||||
@ -344,6 +344,67 @@ describe('Post Email Serializer', function () {
|
|||||||
assert(!output.html.includes('<!--members-only-->'));
|
assert(!output.html.includes('<!--members-only-->'));
|
||||||
assert(!output.html.includes('<!-- PAYWALL -->'));
|
assert(!output.html.includes('<!-- PAYWALL -->'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should hide/show feedback buttons depending on feedback_enabled flag', async function () {
|
||||||
|
sinon.stub(_PostEmailSerializer, 'serializePostModel').callsFake(async () => {
|
||||||
|
return {
|
||||||
|
url: 'https://testpost.com/',
|
||||||
|
title: 'This is a test',
|
||||||
|
excerpt: 'This is a test',
|
||||||
|
authors: 'This is a test',
|
||||||
|
feature_image_alt: 'This is a test',
|
||||||
|
feature_image_caption: 'This is a test',
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
mobiledoc: JSON.stringify({"version":"0.3.1","atoms":[],"cards":[],"markups":[],"sections":[[1,"p",[[0,[],0,"Free content only"]]]],"ghostVersion":"4.0"})
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const customSettings = {
|
||||||
|
accent_color: '#000099',
|
||||||
|
timezone: 'UTC'
|
||||||
|
};
|
||||||
|
|
||||||
|
const settingsMock = sinon.stub(settingsCache, 'get');
|
||||||
|
settingsMock.callsFake(function (key, options) {
|
||||||
|
if (customSettings[key]) {
|
||||||
|
return customSettings[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return settingsMock.wrappedMethod.call(settingsCache, key, options);
|
||||||
|
});
|
||||||
|
const template = {
|
||||||
|
name: 'My newsletter',
|
||||||
|
header_image: '',
|
||||||
|
show_header_icon: true,
|
||||||
|
show_header_title: true,
|
||||||
|
show_feature_image: true,
|
||||||
|
title_font_category: 'sans-serif',
|
||||||
|
title_alignment: 'center',
|
||||||
|
body_font_category: 'serif',
|
||||||
|
show_badge: true,
|
||||||
|
show_header_name: true,
|
||||||
|
feedback_enabled: false,
|
||||||
|
footer_content: 'footer'
|
||||||
|
};
|
||||||
|
const newsletterMock = {
|
||||||
|
get: function (key) {
|
||||||
|
return template[key];
|
||||||
|
},
|
||||||
|
toJSON: function () {
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const output = await serialize({}, newsletterMock, {isBrowserPreview: false});
|
||||||
|
assert(!output.html.includes('%{feedback_button_like}%'));
|
||||||
|
assert(!output.html.includes('%{feedback_button_dislike}%'));
|
||||||
|
|
||||||
|
template.feedback_enabled = true;
|
||||||
|
|
||||||
|
const outputWithButtons = await serialize({}, newsletterMock, {isBrowserPreview: false});
|
||||||
|
assert(outputWithButtons.html.includes('%{feedback_button_like}%'));
|
||||||
|
assert(outputWithButtons.html.includes('%{feedback_button_dislike}%'));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('renderEmailForSegment', function () {
|
describe('renderEmailForSegment', function () {
|
||||||
@ -708,6 +769,7 @@ describe('Post Email Serializer', function () {
|
|||||||
title_alignment: 'center',
|
title_alignment: 'center',
|
||||||
body_font_category: 'serif',
|
body_font_category: 'serif',
|
||||||
show_badge: true,
|
show_badge: true,
|
||||||
|
feedback_enabled: false,
|
||||||
footer_content: 'footer',
|
footer_content: 'footer',
|
||||||
show_header_name: true
|
show_header_name: true
|
||||||
}[key];
|
}[key];
|
||||||
@ -723,6 +785,7 @@ describe('Post Email Serializer', function () {
|
|||||||
titleAlignment: 'center',
|
titleAlignment: 'center',
|
||||||
bodyFontCategory: 'serif',
|
bodyFontCategory: 'serif',
|
||||||
showBadge: true,
|
showBadge: true,
|
||||||
|
feedbackEnabled: false,
|
||||||
footerContent: 'footer',
|
footerContent: 'footer',
|
||||||
accentColor: '#000099',
|
accentColor: '#000099',
|
||||||
adjustedAccentColor: '#000099',
|
adjustedAccentColor: '#000099',
|
||||||
|
Loading…
Reference in New Issue
Block a user