Skip to content

Commit

Permalink
Website: Add script for creating HTML email partials from Markdown ar…
Browse files Browse the repository at this point in the history
…ticles (#7574)

* add to-html-email

* email-preview

* add build-html-email script, update .gitignore

* email preview and email-templates pages, update routes, importer and policies

* add newsletter email layout, build-html-email script, to-html-email updates

* Update .gitignore

* add newsletter emails section to growth handbook

* update scripts

* update fonts in emails

* Update README.md

* Update build-html-email.js

* update comments, add page scripts to layouts

* revert change to build-static-content

* update script

* update email layout and helper

* update handbook entry

* update template data and helper

* update email layout

* update fake data and unsubscribe link

* move added handbook section to the marketing handbook

* rename script

* update colors in unused email template

* update capitalization in helper

* view-email-preview » view-email-template-preview

* Hide emails generated from markdown if they don't exist

* update preview page

* update markdown email directory, create sample article email

* lint fixes, add target="_blank" to links in emails

* update page layouts & styles

* update sample newsletter email, code font, newsletter layout

* Update README.md

* Update view-email-template-preview.js

Co-authored-by: Tim Kern <[email protected]>
Co-authored-by: Mike Thomas <[email protected]>
Co-authored-by: Mike McNeil <[email protected]>
  • Loading branch information
4 people authored Dec 5, 2022
1 parent 2c1ebe4 commit 2d0f33f
Show file tree
Hide file tree
Showing 20 changed files with 991 additions and 8 deletions.
46 changes: 46 additions & 0 deletions handbook/marketing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,52 @@ Use "bcc" so recipients don't see each other's email addresses and send an email

`sails run deliver-release-announcement --emailAddresses='["[email protected]","[email protected]"]'`

### Newsletter emails

The content for our newsletter emails comes from our articles. Because our HTML emails require that the styles are added inline, we generate HTML emails by using a script and manually QA them before sending them out to subscribers.

#### Generating emails for the Fleet newsletter

To convert a Markdown article into an email for the newsletter, you'll need the following:

- A local copy of the [Fleet repo](https://github.com/fleetdm/fleet).
- [Node.js](https://nodejs.org/en/download/)
- (Optional) [Sails.js](https://sailsjs.com) installed globally on your machine (`npm install sails -g`)

Once you have the above follow these steps:

1. Open your terminal program, and navigate to the `website/` folder of your local copy of the Fleet repo.

>Note: If this is your first time running this script, you will need to run `npm install` inside of the `website/` folder to install the website's dependencies.
2. Run the `build-html-email` script and pass in the filename of the Markdown article you would like to convert with the `--articleFilename` flag.

- **With Node**, you will need to use `node ./node_modules/sails/bin/sails run build-html-email` to execute the script. e.g., `node ./node_modules/sails/bin/sails run build-html-email --articleFilename="fleet-4.19.0.md"`
- **With Sails.js installed globally** you can use `sails run build-html-email` to execute the script. e.g., `sails run build-html-email --articleFilename="fleet-4.19.0.md"`

> Note: Only Markdown (`.md`) files are supported by the build-html-email script. The file extension is optional when providing the articleFilename.
4. Once the script is complete, a new email partial will be added to the `website/views/emails/newsletter-partials/` folder.

> Note: If an email partial has already been created from the specified Markdown article, the old version will be overwritten by the new file.
5. Start the website server locally to preview the generated email. To test the changes locally, open a terminal window in the `website/` folder of the Fleet repo and run the following command:

- **With Node.js:** start the server by running `node ./node_modules/sails/bin/sails lift`.
- **With Sails.js installed globally:** start the server by running `sails lift`.

6. With the server lifted, navigate to http://localhost:2024/admin/email-preview and login with the test admin user credentials (email:`[email protected]` pw: `abc123`).

Click on the generated email in the list of emails generated from Markdown content and navigate to the preview page. On this page, you can view the see how the email will look on a variety of screen sizes.

When you've made sure the content of the email looks good at all screen sizes, commit the new email partial to a new branch and open a pull request to add the file. You can request a review from a member of the digital experience team.

**Things to keep in mind when generating newsletter emails:**

- The emails will be generated using the Markdown file locally, any changes present in the local Markdown file will be reflected in the generated email.
- HTML elements in the Markdown file can cause rendering issues when previewing the generated email. If you see a "Script error" overlay while trying to preview an email, reach out to [Eric Shaw](https://github.com/eashaw) for help.
- The filename of the generated email will have periods changed to dashes. e.g., The generated email partial for `fleet-4.19.0.md` would be `fleet-4-19-0.ejs`

### Using Figma

We use Figma for most of our design work. This includes the Fleet product, our website, and our marketing collateral.
Expand Down
133 changes: 133 additions & 0 deletions website/api/controllers/admin/view-email-template-preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
module.exports = {


friendlyName: 'View email template preview',


description: 'Display "email template preview" page.',


urlWildcardSuffix: 'template',


inputs: {

template: {
description: 'The path to an email template, specified in precisely the same way as the equivalent input of the sendTemplateEmail() helper.',
example: 'email-reset-password',
type: 'string',
required: true
},

raw: {
description: 'Whether to return the raw HTML for the email with no JS/CSS (rather than a personalized previewer web page.)',
extendedDescription: 'This can be used from an iframe to allow for accurately previewing email templates without worrying about style interference from the rest of the Sails app.',
type: 'boolean',
}

},

exits: {

success: {
viewTemplatePath: 'pages/admin/email-preview'
},

sendRawHtmlInstead: {
statusCode: 200,
outputType: 'string',
outputDescription: 'The raw HTML for the email as a string.',
},

},


fn: async function ({template, raw}) {

var path = require('path');
var url = require('url');
var util = require('util');
// Determine appropriate email layout and fake data to use.
let layout;
let fakeData;
switch (template) {
case 'internal/email-contact-form':
layout = false;
fakeData = {
contactName: 'Sage',
contactEmail: '[email protected]',
topic: 'Pricing question',
message: 'What is the difference between the "Free" plan and the "Premium" plan?',
};
break;
case 'email-reset-password':
layout = 'layout-email';
fakeData = {
token: '4-32fad81jdaf$329',
};
break;
case 'email-verify-account':
layout = 'layout-email';
fakeData = {
firstName: 'Fleet user',
token: '4-32fad81jdaf$329',
};
break;
case 'email-verify-new-email':
layout = 'layout-email';
fakeData = {
fullName: 'Fleet user',
token: '4-32fad81jdaf$329',
};
break;
case 'email-order-confirmation':
layout = 'layout-email';
fakeData = {
firstName: 'Fleet',
lastName: 'user',
};
break;
default:
layout = 'layout-email-newsletter';
fakeData = {
emailAddress: '[email protected]',
};
}

// Compile HTML template using the appropriate layout.
// > Note that we set the layout, provide access to core `url` package (for
// > building links and image srcs, etc.), and also provide access to core
// > `util` package (for dumping debug data in internal emails).
let emailTemplatePath = path.join('emails/', template);
if (layout) {
layout = path.relative(path.dirname(emailTemplatePath), path.resolve('layouts/', layout));
} else {
layout = false;
}

let sampleHtml = await sails.renderView(
emailTemplatePath,
Object.assign({layout, url, util, _ }, fakeData)
)
.intercept((err)=>{
err.message = 'Whoops, that email template failed to render. Could there be some fake data missing for this particular template in the `switch` statement api/controllers/admin/view-email-template-preview.js? Any chance you need to re-lift the app after making backend changes?\nMore details: '+err.message;
return err;
});

if (raw) {
// Respond with raw, rendered HTML for this email:
throw {sendRawHtmlInstead: sampleHtml};
} else {
// Respond with the previewer page for this email:
return {
sampleHtml,
template,
fakeData,
};
}


}


};
60 changes: 60 additions & 0 deletions website/api/controllers/admin/view-email-templates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
module.exports = {


friendlyName: 'View email templates',


description: 'Display "Email templates" page.',


exits: {

success: {
viewTemplatePath: 'pages/admin/email-templates'
}

},


fn: async function () {


var path = require('path');

// Sniff for top level email templates
let templatePaths = await sails.helpers.fs.ls.with({
dir: path.join(sails.config.paths.views, 'emails/'),
depth: 1,
includeDirs: false,
includeSymlinks: false,
});

let markdownEmailPaths = await sails.helpers.fs.ls.with({
dir: path.join(sails.config.paths.views, 'emails/newsletter'),
depth: 99,
includeDirs: false,
includeSymlinks: false,
});

markdownEmailPaths = markdownEmailPaths.map((templatePath)=>{
let relativePath = path.relative(path.join(sails.config.paths.views, 'emails/'), templatePath);
let extension = path.extname(relativePath);
return _.trimRight(relativePath, extension);
});

templatePaths = templatePaths.map((templatePath)=>{
let relativePath = path.relative(path.join(sails.config.paths.views, 'emails/'), templatePath);
let extension = path.extname(relativePath);
return _.trimRight(relativePath, extension);
});

// Respond with view.
return {
templatePaths,
markdownEmailPaths
};

}


};
Loading

0 comments on commit 2d0f33f

Please sign in to comment.