Added lexical post support to email renderer (#15767)

closes https://github.com/TryGhost/Team/issues/2207

- adds conditional to the post email serializer to switch between
`mobiledocLib` and `lexicalLib` depending on which format the post
contains
This commit is contained in:
Kevin Ansfield 2022-11-04 11:19:40 +00:00 committed by GitHub
parent 2aa0ccde4f
commit 6a573d4511
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 66 additions and 7 deletions

View File

@ -7,6 +7,7 @@ const api = require('../../api').endpoints;
const apiFramework = require('@tryghost/api-framework');
const {URL} = require('url');
const mobiledocLib = require('../../lib/mobiledoc');
const lexicalLib = require('../../lib/lexical');
const htmlToPlaintext = require('@tryghost/html-to-plaintext');
const membersService = require('../members');
const {isUnsplashImage, isLocalContentImage} = require('@tryghost/kg-default-cards/lib/utils');
@ -124,8 +125,8 @@ const PostEmailSerializer = {
// NOTE: serialization is needed to make sure we do post transformations such as image URL transformation from relative to absolute
async serializePostModel(model) {
// fetch mobiledoc rather than html and plaintext so we can render email-specific contents
const frame = {options: {context: {user: true}, formats: 'mobiledoc'}};
// fetch mobiledoc/lexical rather than html and plaintext so we can render email-specific contents
const frame = {options: {context: {user: true}, formats: 'mobiledoc,lexical'}};
const docName = 'posts';
await apiFramework
@ -297,11 +298,17 @@ const PostEmailSerializer = {
post.excerpt = post.excerpt.replace(/\s\[http(.*?)\]/g, '');
}
post.html = mobiledocLib.mobiledocHtmlRenderer.render(
JSON.parse(post.mobiledoc), {target: 'email', postUrl: post.url}
);
if (post.lexical) {
post.html = lexicalLib.lexicalHtmlRenderer.render(
post.lexical, {target: 'email', postUrl: post.url}
);
} else {
post.html = mobiledocLib.mobiledocHtmlRenderer.render(
JSON.parse(post.mobiledoc), {target: 'email', postUrl: post.url}
);
}
// perform any email specific adjustments to the mobiledoc->HTML render output
// perform any email specific adjustments to the HTML render output.
// body wrapper is required so we can get proper top-level selections
const cheerio = require('cheerio');
const _cheerio = cheerio.load(`<body>${post.html}</body>`);
@ -441,7 +448,7 @@ const PostEmailSerializer = {
<h2
style="margin-top: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 1.11em; font-weight: 700; text-rendering: optimizeLegibility; margin: 1.5em 0 0.5em 0; font-size: 26px;">
Subscribe to <span style="white-space: nowrap; font-size: 26px !important;">continue reading.</span></h2>
<p style="margin: 0 auto 1.5em auto; line-height: 1.6em; max-width: 440px;">Become a paid member of ${siteTitle} to get access to all
<p style="margin: 0 auto 1.5em auto; line-height: 1.6em; max-width: 440px;">Become a paid member of ${siteTitle} to get access to all
<span style="white-space: nowrap;">subscriber-only content.</span></p>
<div class="btn btn-accent" style="box-sizing: border-box; width: 100%; display: table;">
<table border="0" cellspacing="0" cellpadding="0" align="center"

View File

@ -520,6 +520,58 @@ describe('Post Email Serializer', function () {
assert(outputWithButtons.html.includes('%{feedback_button_like}%'));
assert(outputWithButtons.html.includes('%{feedback_button_dislike}%'));
});*/
it('handles lexical posts', async function () {
sinon.stub(_PostEmailSerializer, 'serializePostModel').callsFake(async () => {
return {
url: 'https://testpost.com/',
title: 'This is a lexical test',
excerpt: 'This is a lexical test',
authors: 'Mr. Test',
lexical: '{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Bacon ipsum dolor amet porchetta drumstick swine ribeye, tail leberkas beef short loin fatback turducken salami pastrami ball tip shankle ground round. Jowl shankle bacon, short ribs cow ham pork loin meatloaf beef chislic tenderloin.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1},{"altText":"","caption":"🤤","src":"http://localhost:2368/content/images/2022/11/michelle-shelly-captures-it-TJzhTJ2U8Jo-unsplash.jpg","type":"image"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Spare ribs chicken fatback shoulder. Flank swine kielbasa alcatra, porchetta capicola pork loin corned beef short ribs fatback.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1},{"altText":"","caption":"","src":"http://localhost:2368/content/images/2022/11/towfiqu-barbhuiya-yPYOG4_j6YI-unsplash-1.jpg","type":"image"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Prosciutto drumstick porchetta biltong leberkas tri-tip short ribs sausage picanha ham hock. Turducken buffalo venison hamburger landjaeger. Hamburger burgdoggen meatloaf pork belly picanha drumstick salami short ribs ham hock pork loin biltong chicken.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}'
};
});
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,
// Note: we don't need to check the footer content because this should contain valid HTML (not text)
footer_content: '<span>Footer content with valid HTML</span>'
};
const newsletterMock = {
get: function (key) {
return template[key];
},
toJSON: function () {
return template;
}
};
const output = await serialize({}, newsletterMock, {isBrowserPreview: false});
assert(output.html.includes('Bacon ipsum dolor amet'));
assert(output.html.includes('michelle-shelly-captures-it-TJzhTJ2U8Jo-unsplash.jpg'));
});
});
describe('renderEmailForSegment', function () {