Skip to content

Commit

Permalink
Added basic HTML template support to MEGA (#11336)
Browse files Browse the repository at this point in the history
no issue

- Sends formatted email to members
- Added css inlining support for MEGA template
- Migrated MEGA service to use API serializers
- Service needs to be compliant with the API to be able to serve absolute URLs for resources like images
- Fixed send email check for previously sent mails
  • Loading branch information
naz authored and kevinansfield committed Nov 4, 2019
1 parent 424e3ae commit 977298b
Show file tree
Hide file tree
Showing 7 changed files with 305 additions and 69 deletions.
2 changes: 1 addition & 1 deletion core/server/index.js
Expand Up @@ -28,7 +28,7 @@ function initialiseServices() {
apps = require('./services/apps'),
xmlrpc = require('./services/xmlrpc'),
slack = require('./services/slack'),
mega = require('./services/mega'),
{mega} = require('./services/mega'),
webhooks = require('./services/webhooks'),
scheduling = require('./adapters/scheduling');

Expand Down
61 changes: 0 additions & 61 deletions core/server/services/mega.js

This file was deleted.

3 changes: 3 additions & 0 deletions core/server/services/mega/index.js
@@ -0,0 +1,3 @@
module.exports = {
mega: require('./mega')
};
92 changes: 92 additions & 0 deletions core/server/services/mega/mega.js
@@ -0,0 +1,92 @@
const juice = require('juice');
const common = require('../../lib/common');
const api = require('../../api');
const membersService = require('../members');
const bulkEmailService = require('../bulk-email');
const models = require('../../models');
const template = require('./template');
const settingsCache = require('../../services/settings/cache');
const urlUtils = require('../../lib/url-utils');

const getSite = () => {
return Object.assign({}, settingsCache.getPublic(), {
url: urlUtils.urlFor('home', true)
});
};

const sendEmail = async (post) => {
const emailTmpl = {
subject: post.email_subject || post.title,
html: juice(template({post, site: getSite()}))
};

const {members} = await membersService.api.members.list();
const emails = members.map(m => m.email);

return bulkEmailService.send(emailTmpl, emails);
};

// NOTE: serialization is needed to make sure we are using current API and do post transformations
// such as image URL transformation from relative to absolute
const serialize = async (model) => {
const frame = {options: {previous: true, context: {user: true}}};
const apiVersion = model.get('api_version') || 'v3';
const docName = 'posts';

await api.shared
.serializers
.handle
.output(model, {docName: docName, method: 'read'}, api[apiVersion].serializers.output, frame);

return frame.response[docName][0];
};

async function listener(model, options) {
// CASE: do not send email if we import a database
// TODO: refactor post.published events to never fire on importing
if (options && options.importing) {
return;
}

const post = await serialize(model);

if (!post.send_email_when_published) {
return;
}

const deliveredEvents = await models.Action.findAll({
filter: `event:delivered+resource_id:${model.id}`
});

if (deliveredEvents && deliveredEvents.toJSON().length > 0) {
return;
}

sendEmail(post).then(async () => {

let actor = {id: null, type: null};
if (options.context && options.context.user) {
actor = {
id: options.context.user,
type: 'user'
};
}
const action = {
event: 'delivered',
resource_id: model.id,
resource_type: 'post',
actor_id: actor.id,
actor_type: actor.type
};
return models.Action.add(action, {context: {internal: true}});
});
}

function listen() {
common.events.on('post.published', listener);
}

// Public API
module.exports = {
listen: listen
};
140 changes: 140 additions & 0 deletions core/server/services/mega/template.js
@@ -0,0 +1,140 @@
module.exports = ({post, site}) => `
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>${post.title}!</title>
<style>
/* -------------------------------------
RESPONSIVE AND MOBILE FRIENDLY STYLES
------------------------------------- */
@media only screen and (max-width: 620px) {
table[class=body] h1 {
font-size: 28px !important;
margin-bottom: 10px !important;
}
table[class=body] p,
table[class=body] ul,
table[class=body] ol,
table[class=body] td,
table[class=body] span,
table[class=body] a {
font-size: 16px !important;
}
table[class=body] .wrapper,
table[class=body] .article {
padding: 10px !important;
}
table[class=body] .content {
padding: 0 !important;
}
table[class=body] .container {
padding: 0 !important;
width: 100% !important;
}
table[class=body] .main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important;
}
table[class=body] .btn table {
width: 100% !important;
}
table[class=body] .btn a {
width: 100% !important;
}
table[class=body] .img-responsive {
height: auto !important;
max-width: 100% !important;
width: auto !important;
}
}
/* -------------------------------------
PRESERVE THESE STYLES IN THE HEAD
------------------------------------- */
@media all {
.ExternalClass {
width: 100%;
}
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%;
}
.recipient-link a {
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
text-decoration: none !important;
}
#MessageViewBody a {
color: inherit;
text-decoration: none;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
line-height: inherit;
}
}
hr {
border-width: 0;
height: 0;
margin-top: 34px;
margin-bottom: 34px;
border-bottom-width: 1px;
border-bottom-color: #EEF5F8;
}
</style>
</head>
<body class="" style="background-color: #F4F8FB; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.5em; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
<table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background-color: #F4F8FB;">
<tr>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">&nbsp;</td>
<td class="container" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; display: block; margin: 0 auto; max-width: 600px; padding: 10px; width: 600px;">
<div class="content" style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 600px; padding: 30px 20px;">
<!-- START CENTERED WHITE CONTAINER -->
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">Thanks for signing up for ${site.title}!</span>
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #ffffff; border-radius: 8px;">
<!-- START MAIN CONTENT AREA -->
<tr>
<td class="wrapper" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 40px 50px;">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
<tr>
${post.html}
</tr>
</table>
</td>
</tr>
<!-- END MAIN CONTENT AREA -->
</table>
<!-- START FOOTER -->
<div class="footer" style="clear: both; Margin-top: 10px; text-align: center; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
<tr>
<td class="content-block" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; vertical-align: top; padding-bottom: 5px; padding-top: 15px; font-size: 13px; line-height: 19px; color: #738A94; text-align: center;">
If you did not make this request, you can simply delete this message. <br/>You will not be signed up, and no account will be created for you.
</td>
</tr>
</table>
</div>
<!-- END FOOTER -->
<!-- END CENTERED WHITE CONTAINER -->
</div>
</td>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">&nbsp;</td>
</tr>
</table>
</body>
</html>
`;
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -88,6 +88,7 @@
"js-yaml": "3.13.1",
"jsonpath": "1.0.2",
"jsonwebtoken": "8.5.1",
"juice": "5.2.0",
"keypair": "1.0.1",
"knex": "0.19.5",
"knex-migrator": "3.4.0",
Expand Down

0 comments on commit 977298b

Please sign in to comment.