[web] ship updated email templates (#34180)

GitOrigin-RevId: fa04ff132ec898efdb4e2d35df8b8a0b09219d5e
This commit is contained in:
Kristina
2026-06-09 08:05:45 +00:00
committed by Copybot
parent 8c88f53a9a
commit 6d8e4e4c5b
10 changed files with 232 additions and 826 deletions
@@ -1,46 +1,28 @@
import _ from 'lodash'
import { fontFamily, colors } from '../emailStyles.mjs'
export default _.template(`\
<table class="row" style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: left; vertical-align: top; width: 100%;">
<tbody>
<tr style="padding: 0; vertical-align: top;">
<th class="small-12 columns" style="line-height: 1.3; margin: 0 auto; padding: 0; padding-bottom: 16px; padding-left: 16px; padding-right: 16px; text-align: left; width: 564px;">
<table style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%; color: #5D6879; font-family: Helvetica, Arial, sans-serif; font-size: 16px; font-weight: normal; line-height: 1.3;">
<tr style="padding: 0; text-align: left; vertical-align: top;">
<th style="margin: 0; padding: 0; text-align: left;">
<% if (title) { %>
<h3 class="force-overleaf-style" style="margin: 0; color: #5D6879; font-family: Georgia, serif; font-size: 24px; font-weight: normal; line-height: 1.3; padding: 0; text-align: left; word-wrap: normal;">
<%= title %>
</h3>
<% } %>
</th>
<tr>
<td>
<p style="height: 20px; margin: 0; padding: 0;">&#xA0;</p>
<% if (title) { %>
<h2 style="margin: 0 0 16px 0; font-family: ${fontFamily}; font-size: 30px; font-weight: 600; color: ${colors.textDark}; line-height: 40px;">
<%= title %>
</h2>
<% } %>
<% if (greeting) { %>
<p style="margin: 0 0 10px 0; padding: 0;">
<%= greeting %>
</p>
<% } %>
<% if (greeting) { %>
<p style="margin: 0 0 16px 0;">
<%= greeting %>
</p>
<% } %>
<% (message).forEach(function(paragraph) { %>
<p class="force-overleaf-style" style="margin: 0 0 10px 0; padding: 0;">
<%= paragraph %>
</p>
<% }) %>
<% if (highlightedText) { %>
<div style="text-align: center; color: #1B222C; font-size: 20px; margin: 16px 0; padding: 16px 8px; border-radius: 8px; background: #F4F5F6;">
<b><%= highlightedText %></b>
</div>
<% } %>
</td>
</tr>
</tr>
</table>
</th>
</tr>
</tbody>
</table>
\
<% (message).forEach(function(paragraph) { %>
<p class="force-overleaf-style" style="margin: 0 0 16px 0;">
<%= paragraph %>
</p>
<% }) %>
<% if (highlightedText) { %>
<div style="text-align: center; color: ${colors.textDark}; font-size: 20px; margin: 16px 0; padding: 16px 8px; border-radius: 8px; background-color: ${colors.background};">
<b><%= highlightedText %></b>
</div>
<% } %>\
`)
@@ -1,28 +0,0 @@
import _ from 'lodash'
import { fontFamily, colors } from '../emailStyles.mjs'
export default _.template(`\
<% if (title) { %>
<h2 style="margin: 0 0 16px 0; font-family: ${fontFamily}; font-size: 30px; font-weight: 600; color: ${colors.textDark}; line-height: 40px;">
<%= title %>
</h2>
<% } %>
<% if (greeting) { %>
<p style="margin: 0 0 16px 0;">
<%= greeting %>
</p>
<% } %>
<% (message).forEach(function(paragraph) { %>
<p class="force-overleaf-style" style="margin: 0 0 16px 0;">
<%= paragraph %>
</p>
<% }) %>
<% if (highlightedText) { %>
<div style="text-align: center; color: ${colors.textDark}; font-size: 20px; margin: 16px 0; padding: 16px 8px; border-radius: 8px; background-color: ${colors.background};">
<b><%= highlightedText %></b>
</div>
<% } %>\
`)
@@ -1,96 +1,65 @@
import _ from 'lodash'
import { fontFamily, colors } from '../emailStyles.mjs'
export default _.template(`\
<table class="row" style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: left; vertical-align: top; width: 100%;">
<tbody>
<tr style="padding: 0; vertical-align: top;">
<th class="small-12 columns" style="line-height: 1.3; margin: 0 auto; padding: 0; padding-bottom: 16px; padding-left: 16px; padding-right: 16px; text-align: left;">
<table class="cta-table" style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%; color: #5D6879; font-family: Helvetica, Arial, sans-serif; font-size: 16px; font-weight: normal; line-height: 1.3;">
<tr style="padding: 0; text-align: left; vertical-align: top;">
<th style="margin: 0; padding: 0; text-align: left;">
<% if (title) { %>
<h3 class="force-overleaf-style" style="margin: 0; color: #5D6879; font-family: Georgia, serif; font-size: 24px; font-weight: normal; line-height: 1.3; padding: 0; text-align: left; word-wrap: normal;">
<%= title %>
</h3>
<% } %>
</th>
<tr>
<td>
<p style="height: 20px; margin: 0; padding: 0;">&#xA0;</p>
<% if (title) { %>
<h2 style="margin: 0 0 16px 0; font-family: ${fontFamily}; font-size: 30px; font-weight: 600; color: ${colors.textDark}; line-height: 40px;">
<%= title %>
</h2>
<% } %>
<% if (greeting) { %>
<p style="margin: 0 0 10px 0; padding: 0;">
<%= greeting %>
</p>
<% } %>
<% if (greeting) { %>
<p style="margin: 0 0 16px 0;">
<%= greeting %>
</p>
<% } %>
<% (message).forEach(function(paragraph) { %>
<p class="force-overleaf-style" style="margin: 0 0 10px 0; padding: 0;">
<%= paragraph %>
</p>
<% }) %>
<% (message).forEach(function(paragraph) { %>
<p class="force-overleaf-style" style="margin: 0 0 16px 0;">
<%= paragraph %>
</p>
<% }) %>
<p style="margin: 0; padding: 0;">&#xA0;</p>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="margin: 32px 0;">
<tr>
<td bgcolor="${colors.ctaGreen}" style="background-color: ${colors.ctaGreen}; border-radius: 9999px; padding: 6px 16px; text-align: center;">
<a href="<%= ctaURL %>" style="color: ${colors.ctaText}; display: block; font-family: ${fontFamily}; font-size: 16px; font-weight: 600; line-height: 24px; text-align: center; text-decoration: none; -webkit-hyphens: none; -ms-hyphens: none; hyphens: none;">
<%= ctaText %>
</a>
</td>
</tr>
</table>
<table style="border-collapse: collapse; border-spacing: 0; float: none; margin: 0 auto; padding: 0; text-align: center; vertical-align: top; width: auto;">
<tr style="padding: 0; text-align: left; vertical-align: top;">
<td style="-moz-hyphens: auto; -webkit-hyphens: auto; border-collapse: collapse !important; border-radius: 9999px; color: #5D6879; font-family: Helvetica, Arial, sans-serif; font-size: 16px; font-weight: normal; hyphens: auto; line-height: 1.3; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
<table style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
<tr style="padding: 0; text-align: left; vertical-align: top;">
<td style="-moz-hyphens: auto; -webkit-hyphens: auto; background: #4F9C45; border: none; border-collapse: collapse !important; border-radius: 9999px; color: #fefefe; font-family: Helvetica, Arial, sans-serif; font-size: 16px; font-weight: normal; hyphens: auto; line-height: 1.3; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
<a href="<%= ctaURL %>" style="border: 0 solid #4F9C45; border-radius: 9999px; color: #fefefe; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-size: 16px; font-weight: bold; line-height: 1.3; margin: 0; padding: 8px 16px 8px 16px; text-align: left; text-decoration: none;">
<%= ctaText %>
</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
<% if (secondaryMessage && secondaryMessage.length > 0) { %>
<% (secondaryMessage).forEach(function(paragraph) { %>
<p class="force-overleaf-style" style="margin: 0 0 16px 0;">
<%= paragraph %>
</p>
<% }) %>
<% } %>
<% if (secondaryMessage && secondaryMessage.length > 0) { %>
<p style="margin: 0; padding: 0;">&#xA0;</p>
<p style="margin: 0 0 4px 0; font-size: 14px; line-height: 20px; color: ${colors.textMuted};">
If the button above does not appear, please copy and paste this link into your browser's address bar:
</p>
<p style="margin: 0; font-size: 14px; line-height: 20px; word-break: break-all;">
<a href="<%= ctaURL %>" style="color: ${colors.linkGreen}; text-decoration: none;"><%= ctaURL %></a>
</p>
<% (secondaryMessage).forEach(function(paragraph) { %>
<p class="force-overleaf-style">
<%= paragraph %>
</p>
<% }) %>
<% } %>
<p style="margin: 0; padding: 0;">&#xA0;</p>
<p class="force-overleaf-style" style="font-size: 12px;">
If the button above does not appear, please copy and paste this link into your browser's address bar:
</p>
<p class="force-overleaf-style" style="font-size: 12px;">
<%= ctaURL %>
</p>
</td>
</tr>
</tr>
</table>
</th>
</tr>
</tbody>
</table>
<% if (gmailGoToAction) { %>
<script type="application/ld+json">
<%=
StringHelper.stringifyJsonForScript({
"@context": "http://schema.org",
"@type": "EmailMessage",
"potentialAction": {
"@type": "ViewAction",
"target": gmailGoToAction.target,
"url": gmailGoToAction.target,
"name": gmailGoToAction.name
},
"description": gmailGoToAction.description
})
%>
</script>
<% } %>
\
<% if (gmailGoToAction) { %>
<script type="application/ld+json">
<%=
StringHelper.stringifyJsonForScript({
"@context": "http://schema.org",
"@type": "EmailMessage",
"potentialAction": {
"@type": "ViewAction",
"target": gmailGoToAction.target,
"url": gmailGoToAction.target,
"name": gmailGoToAction.name
},
"description": gmailGoToAction.description
})
%>
</script>
<% } %>\
`)
@@ -1,65 +0,0 @@
import _ from 'lodash'
import { fontFamily, colors } from '../emailStyles.mjs'
export default _.template(`\
<% if (title) { %>
<h2 style="margin: 0 0 16px 0; font-family: ${fontFamily}; font-size: 30px; font-weight: 600; color: ${colors.textDark}; line-height: 40px;">
<%= title %>
</h2>
<% } %>
<% if (greeting) { %>
<p style="margin: 0 0 16px 0;">
<%= greeting %>
</p>
<% } %>
<% (message).forEach(function(paragraph) { %>
<p class="force-overleaf-style" style="margin: 0 0 16px 0;">
<%= paragraph %>
</p>
<% }) %>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="margin: 32px 0;">
<tr>
<td bgcolor="${colors.ctaGreen}" style="background-color: ${colors.ctaGreen}; border-radius: 9999px; padding: 6px 16px; text-align: center;">
<a href="<%= ctaURL %>" style="color: ${colors.ctaText}; display: block; font-family: ${fontFamily}; font-size: 16px; font-weight: 600; line-height: 24px; text-align: center; text-decoration: none; -webkit-hyphens: none; -ms-hyphens: none; hyphens: none;">
<%= ctaText %>
</a>
</td>
</tr>
</table>
<% if (secondaryMessage && secondaryMessage.length > 0) { %>
<% (secondaryMessage).forEach(function(paragraph) { %>
<p class="force-overleaf-style" style="margin: 0 0 16px 0;">
<%= paragraph %>
</p>
<% }) %>
<% } %>
<p style="margin: 0 0 4px 0; font-size: 14px; line-height: 20px; color: ${colors.textMuted};">
If the button above does not appear, please copy and paste this link into your browser's address bar:
</p>
<p style="margin: 0; font-size: 14px; line-height: 20px; word-break: break-all;">
<a href="<%= ctaURL %>" style="color: ${colors.linkGreen}; text-decoration: none;"><%= ctaURL %></a>
</p>
<% if (gmailGoToAction) { %>
<script type="application/ld+json">
<%=
StringHelper.stringifyJsonForScript({
"@context": "http://schema.org",
"@type": "EmailMessage",
"potentialAction": {
"@type": "ViewAction",
"target": gmailGoToAction.target,
"url": gmailGoToAction.target,
"name": gmailGoToAction.name
},
"description": gmailGoToAction.description
})
%>
</script>
<% } %>\
`)
@@ -4,12 +4,9 @@ import moment from 'moment'
import EmailMessageHelper from './EmailMessageHelper.mjs'
import StringHelper from '../Helpers/StringHelper.mjs'
import BaseEmailLayout from './Layouts/BaseEmailLayout.mjs'
import UpdatedBaseEmailLayout from './Layouts/UpdatedBaseEmailLayout.mjs'
import SpamSafe from './SpamSafe.mjs'
import ctaEmailBody from './Bodies/cta-email.mjs'
import updatedCtaEmailBody from './Bodies/updated-cta-email.mjs'
import NoCTAEmailBody from './Bodies/NoCTAEmailBody.mjs'
import UpdatedNoCTAEmailBody from './Bodies/UpdatedNoCTAEmailBody.mjs'
function _emailBodyPlainText(content, opts, ctaEmail) {
let emailBody = `${content.greeting(opts, true)}`
@@ -79,10 +76,7 @@ function ctaTemplate(content) {
return content.subject(opts)
},
layout(opts) {
const layoutFn = opts.useNewEmailDesign
? UpdatedBaseEmailLayout
: BaseEmailLayout
return layoutFn(opts)
return BaseEmailLayout(opts)
},
footerMessage(opts) {
return content.footerMessage(opts)
@@ -91,8 +85,7 @@ function ctaTemplate(content) {
return _emailBodyPlainText(content, opts, true)
},
compiledTemplate(opts) {
const bodyFn = opts.useNewEmailDesign ? updatedCtaEmailBody : ctaEmailBody
return bodyFn({
return ctaEmailBody({
title: content.title(opts),
greeting: content.greeting(opts),
message: content.message(opts),
@@ -118,10 +111,7 @@ function NoCTAEmailTemplate(content) {
return content.subject(opts)
},
layout(opts) {
const layoutFn = opts.useNewEmailDesign
? UpdatedBaseEmailLayout
: BaseEmailLayout
return layoutFn(opts)
return BaseEmailLayout(opts)
},
plainTextTemplate(opts) {
return `\
@@ -134,10 +124,7 @@ The ${settings.appName} Team - ${settings.siteUrl}\
`
},
compiledTemplate(opts) {
const bodyFn = opts.useNewEmailDesign
? UpdatedNoCTAEmailBody
: NoCTAEmailBody
return bodyFn({
return NoCTAEmailBody({
title:
typeof content.title === 'function' ? content.title(opts) : undefined,
greeting: content.greeting(opts),
@@ -4,8 +4,6 @@ import logger from '@overleaf/logger'
import EmailBuilder from './EmailBuilder.mjs'
import EmailSender from './EmailSender.mjs'
import Queues from '../../infrastructure/Queues.mjs'
import SplitTestHandler from '../SplitTests/SplitTestHandler.mjs'
import UserGetter from '../User/UserGetter.mjs'
const EMAIL_SETTINGS = Settings.email || {}
@@ -14,27 +12,6 @@ const EMAIL_SETTINGS = Settings.email || {}
* @param {object} opts
*/
async function sendEmail(emailType, opts) {
if (opts.to) {
try {
const user = await UserGetter.promises.getUserByAnyEmail(opts.to, {
_id: 1,
})
if (user) {
const { variant } =
await SplitTestHandler.promises.getAssignmentForUser(
user._id.toString(),
'new-email-design'
)
opts.useNewEmailDesign = variant === 'enabled'
}
} catch (err) {
logger.warn(
{ err },
'failed to get new-email-design split test assignment'
)
}
}
const email = EmailBuilder.buildEmail(emailType, opts)
if (email.type === 'lifecycle' && !EMAIL_SETTINGS.lifecycle) {
return
@@ -1,395 +1,169 @@
import _ from 'lodash'
import settings from '@overleaf/settings'
import { fontFamily, colors } from '../emailStyles.mjs'
const _isSaas = settings.env === 'saas'
const _imageBase = settings.cdn?.web?.host || settings.siteUrl || null
let _logoHtml = `<span style="font-family: ${fontFamily}; font-size: 30px; font-weight: bold; color: ${colors.logoGreen};">${settings.appName}</span>`
let _taglineHtml = ''
if (_isSaas && _imageBase) {
_logoHtml = `
<a href="${settings.siteUrl}" style="text-decoration: none; display: inline-block; border: 0;">
<img
src="${_imageBase}/img/ol-brand/email-logo@2x.png"
alt="${settings.appName}"
width="139"
height="40"
style="display: block; border: 0; width: 139px; height: 40px;"
>
</a>
`
_taglineHtml = `
<!--[if mso]>
<img
src="${_imageBase}/img/ol-brand/email-footer-tagline@2x.png"
alt="The home of scientific and technical writing"
width="452"
height="25"
style="display: block; border: 0;"
/>
<![endif]-->
<!--[if !mso]><!-->
<img
src="${_imageBase}/img/ol-brand/email-footer-tagline@2x.png"
alt="The home of scientific and technical writing"
style="display: block; border: 0; width: 100%; max-width: 452px; height: auto;"
>
<!--<![endif]-->
`
}
const _customFooter = settings.email?.template?.customFooter || ''
export default _.template(`\
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="x-apple-disable-message-reformatting">
<!--[if mso]>
<style type="text/css">
body, table, td, p, a, li { font-family: Arial, Helvetica, sans-serif !important; }
</style>
<![endif]-->
<style>
body { margin: 0; padding: 0; background-color: ${colors.background}; }
img { border: 0; display: block; outline: none; text-decoration: none; }
.email-layout-table { border-collapse: collapse !important; }
a[x-apple-data-detectors] { color: inherit !important; text-decoration: none !important; }
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" style="Margin: 0; background: #E4E8EE !important; margin: 0; min-height: 100%; padding: 0;">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width">
<style>.button td {
border-radius: 9999px; }
.force-overleaf-style a,
.force-overleaf-style a[href] {
color: ${colors.linkGreen} !important;
text-decoration: underline !important;
-moz-hyphens: none;
-ms-hyphens: none;
-webkit-hyphens: none;
hyphens: none;
}
.force-overleaf-style a:visited,
.force-overleaf-style a[href]:visited { color: ${colors.linkGreen}; }
.force-overleaf-style a:hover,
.force-overleaf-style a[href]:hover { color: ${colors.linkHover}; }
.force-overleaf-style a,
.force-overleaf-style a[href] {
color: #138A07 !important;
text-decoration: none !important;
-moz-hyphens: none;
-ms-hyphens: none;
-webkit-hyphens: none;
hyphens: none; }
.force-overleaf-style a:visited,
.force-overleaf-style a[href]:visited {
color: #138A07; }
.force-overleaf-style a:hover,
.force-overleaf-style a[href]:hover {
color: #3d7935; }
.force-overleaf-style a:active,
.force-overleaf-style a[href]:active {
color: #3d7935; }
</style>
<style>@media only screen {
html {
min-height: 100%;
background: #f6f6f6;
}
}
@media only screen and (min-width: 621px) {
.email-card-inner { padding: 56px !important; }
}
@media only screen and (max-width: 620px) {
.email-container { width: 100% !important; max-width: 100% !important; }
.email-outer-cell { padding: 0 10px !important; }
.email-header-cell { padding: 16px 0 !important; }
}
</style>
</head>
<body bgcolor="${colors.background}" style="margin: 0; padding: 0; background-color: ${colors.background}; font-family: ${fontFamily};">
@media only screen and (max-width: 596px) {
.small-float-center {
margin: 0 auto !important;
float: none !important;
text-align: center !important;
}
<!-- Prevent Gmail iOS font size manipulation -->
<div style="display: none; white-space: nowrap; font: 15px courier; line-height: 0;">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</div>
.small-text-center {
text-align: center !important;
}
<table class="email-layout-table" role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="${colors.background}" style="background-color: ${colors.background}; min-width: 100%;">
<tr>
<td class="email-outer-cell" align="center" valign="top" style="padding: 40px 10px;">
.small-text-left {
text-align: left !important;
}
<!--[if mso]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="600" align="center"><tr><td>
<![endif]-->
<table class="email-container email-layout-table" role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;">
.small-text-right {
text-align: right !important;
}
<!-- Logo header -->
<tr>
<td class="email-header-cell" style="padding: 0 0 24px 0;">
${_logoHtml}
</td>
</tr>
.cta-table {
table-layout: fixed;
}
}
<!-- White content card -->
<tr>
<td bgcolor="#ffffff" style="background-color: #ffffff; border-radius: 8px; overflow: hidden;">
<table class="email-layout-table" role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td
class="email-card-inner"
style="padding: 32px 24px; font-family: ${fontFamily}; font-size: 16px; line-height: 1.5; color: ${colors.textDark};"
>
<%= body %>
</td>
</tr>
</table>
</td>
</tr>
@media only screen and (max-width: 596px) {
.hide-for-large {
display: block !important;
width: auto !important;
overflow: visible !important;
max-height: none !important;
font-size: inherit !important;
line-height: inherit !important;
}
}
<!-- Footer spacer -->
<tr>
<td style="height: 24px; font-size: 24px; line-height: 24px; mso-line-height-rule: exactly;">&nbsp;</td>
</tr>
@media only screen and (max-width: 596px) {
table.body table.container .hide-for-large,
table.body table.container .row.hide-for-large {
display: table !important;
width: 100% !important;
}
}
<!-- Footer tagline -->
<tr>
<td align="center" style="padding: 0 0 48px 0;">
${_taglineHtml}
</td>
</tr>
@media only screen and (max-width: 596px) {
table.body table.container .callout-inner.hide-for-large {
display: table-cell !important;
width: 100% !important;
}
}
<!-- Footer site info -->
${
_customFooter
? `<tr>
<td align="center" style="padding: 0 0 32px 0; font-family: ${fontFamily}; font-size: 13px; color: ${colors.textMuted}; line-height: 1.5;">
${_customFooter}
</td>
</tr>`
: ''
}
@media only screen and (max-width: 596px) {
table.body table.container .show-for-large {
display: none !important;
width: 0;
mso-hide: all;
overflow: hidden;
}
}
<% if (footerMessage) { %>
<tr>
<td align="center" class="force-overleaf-style" style="padding: 0 0 32px 0; font-family: ${fontFamily}; font-size: 14px; color: ${colors.textMuted}; line-height: 20px;">
<%= footerMessage %>
</td>
</tr>
<% } %>
@media only screen and (max-width: 596px) {
table.body img {
width: auto;
height: auto;
}
</table>
<!--[if mso]>
</td></tr></table>
<![endif]-->
table.body center {
min-width: 0 !important;
}
</td>
</tr>
</table>
table.body .container {
width: 95% !important;
}
table.body .columns,
table.body .column {
height: auto !important;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
padding-left: 16px !important;
padding-right: 16px !important;
}
table.body .columns .column,
table.body .columns .columns,
table.body .column .column,
table.body .column .columns {
padding-left: 0 !important;
padding-right: 0 !important;
}
table.body .collapse .columns,
table.body .collapse .column {
padding-left: 0 !important;
padding-right: 0 !important;
}
td.small-1,
th.small-1 {
display: inline-block !important;
width: 8.33333% !important;
}
td.small-2,
th.small-2 {
display: inline-block !important;
width: 16.66667% !important;
}
td.small-3,
th.small-3 {
display: inline-block !important;
width: 25% !important;
}
td.small-4,
th.small-4 {
display: inline-block !important;
width: 33.33333% !important;
}
td.small-5,
th.small-5 {
display: inline-block !important;
width: 41.66667% !important;
}
td.small-6,
th.small-6 {
display: inline-block !important;
width: 50% !important;
}
td.small-7,
th.small-7 {
display: inline-block !important;
width: 58.33333% !important;
}
td.small-8,
th.small-8 {
display: inline-block !important;
width: 66.66667% !important;
}
td.small-9,
th.small-9 {
display: inline-block !important;
width: 75% !important;
}
td.small-10,
th.small-10 {
display: inline-block !important;
width: 83.33333% !important;
}
td.small-11,
th.small-11 {
display: inline-block !important;
width: 91.66667% !important;
}
td.small-12,
th.small-12 {
display: inline-block !important;
width: 100% !important;
}
.columns td.small-12,
.column td.small-12,
.columns th.small-12,
.column th.small-12 {
display: block !important;
width: 100% !important;
}
table.body td.small-offset-1,
table.body th.small-offset-1 {
margin-left: 8.33333% !important;
Margin-left: 8.33333% !important;
}
table.body td.small-offset-2,
table.body th.small-offset-2 {
margin-left: 16.66667% !important;
Margin-left: 16.66667% !important;
}
table.body td.small-offset-3,
table.body th.small-offset-3 {
margin-left: 25% !important;
Margin-left: 25% !important;
}
table.body td.small-offset-4,
table.body th.small-offset-4 {
margin-left: 33.33333% !important;
Margin-left: 33.33333% !important;
}
table.body td.small-offset-5,
table.body th.small-offset-5 {
margin-left: 41.66667% !important;
Margin-left: 41.66667% !important;
}
table.body td.small-offset-6,
table.body th.small-offset-6 {
margin-left: 50% !important;
Margin-left: 50% !important;
}
table.body td.small-offset-7,
table.body th.small-offset-7 {
margin-left: 58.33333% !important;
Margin-left: 58.33333% !important;
}
table.body td.small-offset-8,
table.body th.small-offset-8 {
margin-left: 66.66667% !important;
Margin-left: 66.66667% !important;
}
table.body td.small-offset-9,
table.body th.small-offset-9 {
margin-left: 75% !important;
Margin-left: 75% !important;
}
table.body td.small-offset-10,
table.body th.small-offset-10 {
margin-left: 83.33333% !important;
Margin-left: 83.33333% !important;
}
table.body td.small-offset-11,
table.body th.small-offset-11 {
margin-left: 91.66667% !important;
Margin-left: 91.66667% !important;
}
table.body table.columns td.expander,
table.body table.columns th.expander {
display: none !important;
}
table.body .right-text-pad,
table.body .text-pad-right {
padding-left: 10px !important;
}
table.body .left-text-pad,
table.body .text-pad-left {
padding-right: 10px !important;
}
table.menu {
width: 100% !important;
}
table.menu td,
table.menu th {
width: auto !important;
display: inline-block !important;
}
table.menu.vertical td,
table.menu.vertical th,
table.menu.small-vertical td,
table.menu.small-vertical th {
display: block !important;
}
table.menu[align="center"] {
width: auto !important;
}
table.button.small-expand,
table.button.small-expanded {
width: 100% !important;
}
table.button.small-expand table,
table.button.small-expanded table {
width: 100%;
}
table.button.small-expand table a,
table.button.small-expanded table a {
text-align: center !important;
width: 100% !important;
padding-left: 0 !important;
padding-right: 0 !important;
}
table.button.small-expand center,
table.button.small-expanded center {
min-width: 0;
}
}</style>
</head>
<body leftmargin="0" topmargin="0" marginwidth="0" marginheight="0" bgcolor="#F6F6F6" style="-moz-box-sizing: border-box; -ms-text-size-adjust: 100%; -webkit-box-sizing: border-box; -webkit-text-size-adjust: 100%; Margin: 0; box-sizing: border-box; color: #5D6879; font-family: Helvetica, Arial, sans-serif; font-size: 16px; font-weight: normal; line-height: 1.3; margin: 0; min-width: 100%; padding: 0; text-align: left; width: 100% !important;">
<!-- <span class="preheader"></span> -->
<table class="body" border="0" cellspacing="0" cellpadding="0" width="100%" height="100%" style="Margin: 0; background: #E4E8EE; border-collapse: collapse; border-spacing: 0; color: #5D6879; font-family: Helvetica, Arial, sans-serif; font-size: 16px; font-weight: normal; height: 100%; line-height: 1.3; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
<tr style="padding: 0; text-align: left; vertical-align: top;">
<td class="body-cell" align="center" valign="top" bgcolor="#F6F6F6" style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; background: #E4E8EE !important; border-collapse: collapse !important; color: #5D6879; font-family: Helvetica, Arial, sans-serif; font-size: 16px; font-weight: normal; hyphens: auto; line-height: 1.3; margin: 0; padding: 0; padding-bottom: 20px; text-align: left; vertical-align: top; word-wrap: break-word;">
<center data-parsed="" style="min-width: 580px; width: 100%;">
<table align="center" class="wrapper header float-center" style="Margin: 0 auto; background: #1E2530; border-bottom: none; border-collapse: collapse; border-spacing: 0; float: none; margin: 0 auto; padding: 0; text-align: center; vertical-align: top; width: 100%;"><tr style="padding: 0; text-align: left; vertical-align: top;"><td class="wrapper-inner" style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #5D6879; font-family: Helvetica, Arial, sans-serif; font-size: 16px; font-weight: normal; hyphens: auto; line-height: 1.3; margin: 0; padding: 20px; text-align: left; vertical-align: top; word-wrap: break-word;">
<table align="center" class="container" style="Margin: 0 auto; background: transparent; border-collapse: collapse; border-spacing: 0; margin: 0 auto; padding: 0; text-align: inherit; vertical-align: top; width: 580px;"><tbody><tr style="padding: 0; text-align: left; vertical-align: top;"><td style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #5D6879; font-family: Helvetica, Arial, sans-serif; font-size: 16px; font-weight: normal; hyphens: auto; line-height: 1.3; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
<table class="row collapse" style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: left; vertical-align: top; width: 100%;"><tbody><tr style="padding: 0; text-align: left; vertical-align: top;">
<th class="small-12 large-12 columns first last" style="Margin: 0 auto; color: #5D6879; font-family: Helvetica, Arial, sans-serif; font-size: 16px; font-weight: normal; line-height: 1.3; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 588px;"><table style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;"><tr style="padding: 0; text-align: left; vertical-align: top;"><th style="Margin: 0; color: #5D6879; font-family: Helvetica, Arial, sans-serif; font-size: 16px; font-weight: normal; line-height: 1.3; margin: 0; padding: 0; text-align: left;">
<h1 style="Margin: 0; Margin-bottom: px; color: #FFFFFF; font-family: Georgia, serif; font-size: 30px; font-weight: normal; line-height: 1.3; margin: 0; margin-bottom: px; padding: 0; text-align: left; word-wrap: normal;">
${settings.appName}
</h1>
</th>
<th class="expander" style="Margin: 0; color: #5D6879; font-family: Helvetica, Arial, sans-serif; font-size: 16px; font-weight: normal; line-height: 1.3; margin: 0; padding: 0 !important; text-align: left; visibility: hidden; width: 0;"></th></tr></table></th>
</tr></tbody></table>
</td></tr></tbody></table>
</td></tr></table>
<table class="spacer float-center" style="Margin: 0 auto; border-collapse: collapse; border-spacing: 0; float: none; margin: 0 auto; padding: 0; text-align: center; vertical-align: top; width: 100%;"><tbody><tr style="padding: 0; text-align: left; vertical-align: top;"><td height="20px" style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #5D6879; font-family: Helvetica, Arial, sans-serif; font-size: 20px; font-weight: normal; hyphens: auto; line-height: 20px; margin: 0; mso-line-height-rule: exactly; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">&#xA0;</td></tr></tbody></table>
<table align="center" class="container main float-center" style="Margin: 0 auto; Margin-top: 10px; background: #FFFFFF; border-collapse: collapse; border-spacing: 0; float: none; margin: 0 auto; margin-top: 10px; padding: 0; text-align: center; vertical-align: top; width: 580px;"><tbody><tr style="padding: 0; text-align: left; vertical-align: top;"><td style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #5D6879; font-family: Helvetica, Arial, sans-serif; font-size: 16px; font-weight: normal; hyphens: auto; line-height: 1.3; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
<table class="spacer" style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;"><tbody><tr style="padding: 0; text-align: left; vertical-align: top;"><td height="20px" style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #5D6879; font-family: Helvetica, Arial, sans-serif; font-size: 20px; font-weight: normal; hyphens: auto; line-height: 20px; margin: 0; mso-line-height-rule: exactly; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">&#xA0;</td></tr></tbody></table>
<%= body %>
<table class="wrapper secondary" align="center" style="background: #E4E8EE; border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;"><tr style="padding: 0; text-align: left; vertical-align: top;"><td class="wrapper-inner" style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #5D6879; font-family: Helvetica, Arial, sans-serif; font-size: 16px; font-weight: normal; hyphens: auto; line-height: 1.3; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
<table class="spacer" style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;"><tbody><tr style="padding: 0; text-align: left; vertical-align: top;"><td height="10px" style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #5D6879; font-family: Helvetica, Arial, sans-serif; font-size: 10px; font-weight: normal; hyphens: auto; line-height: 10px; margin: 0; mso-line-height-rule: exactly; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">&#xA0;</td></tr></tbody></table>
<p style="Margin: 0; Margin-bottom: 10px; color: #5D6879; font-family: Helvetica, Arial, sans-serif; font-size: 16px; font-weight: normal; line-height: 1.3; margin: 0; margin-bottom: 10px; padding: 0; text-align: left;"><small style="color: #5D6879; font-size: 80%;">
${
settings.email &&
settings.email.template &&
settings.email.template.customFooter
? `${settings.email.template.customFooter}<br>`
: ''
}${settings.appName} &bull; <a href="${
settings.siteUrl
}" style="Margin: 0; color: #0F7A06; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; padding: 0; text-align: left; text-decoration: none;">${
settings.siteUrl
}</a>
</small></p>
<% if (footerMessage) { %><p class="force-overleaf-style" style="font-size: 12px; text-align: center;"><%= footerMessage %></p><% } %>
</td></tr></table>
</td></tr></tbody></table>
</center>
</td>
</tr>
</table>
<!-- prevent Gmail on iOS font size manipulation -->
<div style="display:none; white-space:nowrap; font:15px courier; line-height:0;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </div>
</body>
</body>
</html>\
`)
@@ -1,169 +0,0 @@
import _ from 'lodash'
import settings from '@overleaf/settings'
import { fontFamily, colors } from '../emailStyles.mjs'
const _isSaas = settings.env === 'saas'
const _imageBase = settings.cdn?.web?.host || settings.siteUrl || null
let _logoHtml = `<span style="font-family: ${fontFamily}; font-size: 30px; font-weight: bold; color: ${colors.logoGreen};">${settings.appName}</span>`
let _taglineHtml = ''
if (_isSaas && _imageBase) {
_logoHtml = `
<a href="${settings.siteUrl}" style="text-decoration: none; display: inline-block; border: 0;">
<img
src="${_imageBase}/img/ol-brand/email-logo@2x.png"
alt="${settings.appName}"
width="139"
height="40"
style="display: block; border: 0; width: 139px; height: 40px;"
>
</a>
`
_taglineHtml = `
<!--[if mso]>
<img
src="${_imageBase}/img/ol-brand/email-footer-tagline@2x.png"
alt="The home of scientific and technical writing"
width="452"
height="25"
style="display: block; border: 0;"
/>
<![endif]-->
<!--[if !mso]><!-->
<img
src="${_imageBase}/img/ol-brand/email-footer-tagline@2x.png"
alt="The home of scientific and technical writing"
style="display: block; border: 0; width: 100%; max-width: 452px; height: auto;"
>
<!--<![endif]-->
`
}
const _customFooter = settings.email?.template?.customFooter || ''
export default _.template(`\
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="x-apple-disable-message-reformatting">
<!--[if mso]>
<style type="text/css">
body, table, td, p, a, li { font-family: Arial, Helvetica, sans-serif !important; }
</style>
<![endif]-->
<style>
body { margin: 0; padding: 0; background-color: ${colors.background}; }
img { border: 0; display: block; outline: none; text-decoration: none; }
.email-layout-table { border-collapse: collapse !important; }
a[x-apple-data-detectors] { color: inherit !important; text-decoration: none !important; }
.force-overleaf-style a,
.force-overleaf-style a[href] {
color: ${colors.linkGreen} !important;
text-decoration: underline !important;
-moz-hyphens: none;
-ms-hyphens: none;
-webkit-hyphens: none;
hyphens: none;
}
.force-overleaf-style a:visited,
.force-overleaf-style a[href]:visited { color: ${colors.linkGreen}; }
.force-overleaf-style a:hover,
.force-overleaf-style a[href]:hover { color: ${colors.linkHover}; }
@media only screen and (min-width: 621px) {
.email-card-inner { padding: 56px !important; }
}
@media only screen and (max-width: 620px) {
.email-container { width: 100% !important; max-width: 100% !important; }
.email-outer-cell { padding: 0 10px !important; }
.email-header-cell { padding: 16px 0 !important; }
}
</style>
</head>
<body bgcolor="${colors.background}" style="margin: 0; padding: 0; background-color: ${colors.background}; font-family: ${fontFamily};">
<!-- Prevent Gmail iOS font size manipulation -->
<div style="display: none; white-space: nowrap; font: 15px courier; line-height: 0;">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</div>
<table class="email-layout-table" role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="${colors.background}" style="background-color: ${colors.background}; min-width: 100%;">
<tr>
<td class="email-outer-cell" align="center" valign="top" style="padding: 40px 10px;">
<!--[if mso]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="600" align="center"><tr><td>
<![endif]-->
<table class="email-container email-layout-table" role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;">
<!-- Logo header -->
<tr>
<td class="email-header-cell" style="padding: 0 0 24px 0;">
${_logoHtml}
</td>
</tr>
<!-- White content card -->
<tr>
<td bgcolor="#ffffff" style="background-color: #ffffff; border-radius: 8px; overflow: hidden;">
<table class="email-layout-table" role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td
class="email-card-inner"
style="padding: 32px 24px; font-family: ${fontFamily}; font-size: 16px; line-height: 1.5; color: ${colors.textDark};"
>
<%= body %>
</td>
</tr>
</table>
</td>
</tr>
<!-- Footer spacer -->
<tr>
<td style="height: 24px; font-size: 24px; line-height: 24px; mso-line-height-rule: exactly;">&nbsp;</td>
</tr>
<!-- Footer tagline -->
<tr>
<td align="center" style="padding: 0 0 48px 0;">
${_taglineHtml}
</td>
</tr>
<!-- Footer site info -->
${
_customFooter
? `<tr>
<td align="center" style="padding: 0 0 32px 0; font-family: ${fontFamily}; font-size: 13px; color: ${colors.textMuted}; line-height: 1.5;">
${_customFooter}
</td>
</tr>`
: ''
}
<% if (footerMessage) { %>
<tr>
<td align="center" class="force-overleaf-style" style="padding: 0 0 32px 0; font-family: ${fontFamily}; font-size: 14px; color: ${colors.textMuted}; line-height: 20px;">
<%= footerMessage %>
</td>
</tr>
<% } %>
</table>
<!--[if mso]>
</td></tr></table>
<![endif]-->
</td>
</tr>
</table>
</body>
</html>\
`)
@@ -4,7 +4,7 @@ import path from 'node:path'
const LAYOUT_PATH = path.join(
import.meta.dirname,
'../../../../app/src/Features/Email/Layouts/UpdatedBaseEmailLayout'
'../../../../app/src/Features/Email/Layouts/BaseEmailLayout'
)
async function buildLayoutWithEnv(env) {
@@ -20,7 +20,7 @@ async function buildLayoutWithEnv(env) {
return layout({ body: 'Test body', footerMessage: '' })
}
describe('UpdatedBaseEmailLayout branding', function () {
describe('BaseEmailLayout branding', function () {
afterEach(function () {
vi.restoreAllMocks()
vi.resetModules()
@@ -3,9 +3,6 @@ import cheerio from 'cheerio'
import path from 'node:path'
import EmailMessageHelper from '../../../../app/src/Features/Email/EmailMessageHelper.mjs'
import ctaEmailBody from '../../../../app/src/Features/Email/Bodies/cta-email.mjs'
import NoCTAEmailBody from '../../../../app/src/Features/Email/Bodies/NoCTAEmailBody.mjs'
import BaseEmailLayout from '../../../../app/src/Features/Email/Layouts/BaseEmailLayout.mjs'
const MODULE_PATH = path.join(
import.meta.dirname,
@@ -23,24 +20,6 @@ describe('EmailBuilder', function () {
default: EmailMessageHelper,
}))
vi.doMock('../../../../app/src/Features/Email/Bodies/cta-email', () => ({
default: ctaEmailBody,
}))
vi.doMock(
'../../../../app/src/Features/Email/Bodies/NoCTAEmailBody',
() => ({
default: NoCTAEmailBody,
})
)
vi.doMock(
'../../../../app/src/Features/Email/Layouts/BaseEmailLayout',
() => ({
default: BaseEmailLayout,
})
)
vi.doMock('@overleaf/settings', () => ({
default: ctx.settings,
}))