Files
fleet/website/api/controllers/admin/view-email-template-preview.js
Eric b911c82c30 Website: Update application form submission action (#46403)
Closes: https://github.com/fleetdm/confidential/issues/16057

Changes:
- Added `email-application-submitted`, an email template that is used to
reply to users who fill out the "Apply" form
- Updated the `deliver-application-submission` action to return an
`invalidEmailDomain` response if a user's email address is on the
bannedEmailDomainsForContactFormSubmissions list, and to send a "Thank
you for applying to Fleet" email to the user who submitted the form.
- Stubbed a new custom config variable: `applicationReplyEmailAddress`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Applicants receive a "Thanks for applying to Fleet" confirmation email
with a personalized greeting and ~7-day expectation.
* Added an application-submitted email template and preview using the
email layout.

* **Improvements**
* Added a hidden honeypot field to the application form; submissions
with it filled are silently discarded.
* Added email-domain validation with a specific form error and clearer
error rendering.
  * Redirected jobs to the internal handbook open-positions section.

* **Chores**
* Added a commented placeholder for an application reply email setting.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-02 12:36:26 -05:00

268 lines
8.1 KiB
JavaScript
Vendored

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 moment = require('moment');
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: 'sage@example.com',
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;
case 'email-subscription-renewal-confirmation':
layout = 'layout-email';
fakeData = {
firstName: 'Fleet',
lastName: 'user',
};
break;
case 'email-upcoming-subscription-renewal':
layout = 'layout-email';
fakeData = {
firstName: 'Fleet',
lastName: 'user',
subscriptionPriceInWholeDollars: 60,
numberOfHosts: 10,
subscriptionCostPerHost: 6,
nextBillingAt: Date.now() + (1000 * 60 * 60 * 24 * 7),
};
break;
case 'email-signed-csr-for-apns':
layout = 'layout-email';
fakeData = {};
break;
case 'email-sandbox-ready-approved':
layout = 'layout-email';
fakeData = {};
break;
case 'email-nurture-stage-three':
layout = 'layout-nurture-email';
fakeData = {
firstName: 'Sage',
emailAddress: 'sage@example.com',
};
break;
case 'email-nurture-stage-four':
layout = 'layout-nurture-email';
fakeData = {
firstName: 'Sage',
emailAddress: 'sage@example.com',
};
break;
case 'email-nurture-stage-five':
layout = 'layout-nurture-email';
fakeData = {
firstName: 'Sage',
emailAddress: 'sage@example.com',
};
break;
case 'email-fleet-premium-trial':
// layout = 'layout-nurture-email';
fakeData = {
firstName: 'Sage',
emailAddress: 'sage@example.com',
};
break;
case 'email-deal-registration':
layout = 'layout-email';
fakeData = {
submittersFirstName: 'Jane',
submittersLastName: 'Williamson',
submittersEmailAddress: 'jane@example.com',
submittersOrganization: 'Fake organization',
customersName: 'Sage Scorpion',
customersEmailAddress: 'sage@example.com',
customersOrganization: 'Fake organization 2',
dealStage: 'Identified',
submitterIsExistingPartner: 'yes',
expectedClose: '1 - 3 months',
numberOfHosts: '1000 - 5000',
platforms: `<br> Apple (macOS, iOS/iPadOS) <br> Linux`,
useCase: `<br> Device management <br> Security / compliance`,
notes: 'Fake organization 2 is looking for a managed cloud MDM solution with a name that ends with "eet"',
};
break;
case 'email-partner-registration':
layout = 'layout-email';
fakeData = {
submittersFirstName: 'Jane',
submittersLastName: 'Williamson',
submittersEmailAddress: 'jane@example.com',
submittersOrganization: 'Fake organization',
partnerType: 'reseller',
partnerWebsite: 'example.com',
partnerCountry: 'US',
goal: 'Resell or manage devices for customers',
notes: 'My organization is looking to resell an MDM solution to our cloud customers',
numberOfHosts: '100 - 1000',
servicesOffered: '<br> IT services / MSP<br> MDM / endpoint management'
};
break;
case 'email-contact-form':
fakeData = {
firstName: 'Jane',
lastName: 'Williamson',
emailAddress: 'jane@example.com',
message: 'Hi, this is a contact form message!',
};
break;
case 'email-fleet-premium-local-trial-started':
layout = 'layout-nurture-email';
fakeData = {
firstName: 'Sage',
emailAddress: 'sage@example.com',
};
break;
case 'email-fleet-premium-local-trial-ended':
layout = 'layout-nurture-email';
fakeData = {
firstName: 'Sage',
emailAddress: 'sage@example.com',
};
break;
case 'email-fleet-premium-pov-trial-started':
layout = 'layout-nurture-email';
fakeData = {
firstName: 'Sage',
emailAddress: 'sage@example.com',
};
break;
case 'email-fleet-premium-pov-trial-ended':
layout = 'layout-nurture-email';
fakeData = {
firstName: 'Sage',
emailAddress: 'sage@example.com',
};
break;
case 'email-application-submitted':
layout = 'layout-email';
fakeData = {
firstName: 'Sage',
};
break;
default:
layout = 'layout-email-newsletter';
fakeData = {
emailAddress: 'sage@example.com',
firstName: 'Sage',
lastName: 'Scorpion',
};
}
// 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, _, moment }, 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,
};
}
}
};