WIP
This commit is contained in:
parent
ab258892ea
commit
96c0e69cee
|
@ -0,0 +1,356 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<!-- Cutom Font Embed CSS -->
|
||||
<link href="http://fonts.googleapis.com/css?family=Open+Sans:400,600,700" rel="stylesheet" type="text/css" />
|
||||
<title>Pure VPN</title>
|
||||
</head>
|
||||
<body
|
||||
leftmargin="0"
|
||||
marginwidth="0"
|
||||
topmargin="0"
|
||||
marginheight="0"
|
||||
offset="0"
|
||||
style="
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
background-color: #ffffff;
|
||||
height: 100% !important;
|
||||
width: 100% !important;
|
||||
"
|
||||
>
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
max-width: 600px;
|
||||
font-family: Verdana, Geneva, sans-serif;
|
||||
color: #2b2b2b;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
background: url('https://images.purevpn-tools.com/public/images/2791_eml_pur_bg_dull.png') #fff
|
||||
no-repeat top;
|
||||
background-size: contain;
|
||||
"
|
||||
>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 48px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center">
|
||||
<img src="https://images.purevpn-tools.com/public/images/2791_eml_pure_logo.png" alt="logo" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 52px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
width="100%"
|
||||
style="max-width: 450px; margin: auto; padding: 0 15px"
|
||||
>
|
||||
<tr>
|
||||
<td style="color: #000; font-size: 22px; font-weight: bold; line-height: 34px">
|
||||
Hi there!
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #78747b; font-size: 16px; font-weight: normal; line-height: 24px">
|
||||
You have been asked by {{ invitedBy }} to confirm your membership with the PurePass
|
||||
organization <strong>{{ orgName }}</strong>.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 30px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ acceptInviteUrl }}"
|
||||
><button
|
||||
style="
|
||||
align-items: center;
|
||||
padding: 23px 50px;
|
||||
height: 60px;
|
||||
background: #28c675;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
line-height: 100%;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
"
|
||||
>
|
||||
Confirm Membership
|
||||
</button></a
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 40px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="border-bottom: 2px dashed #d9d3de"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 40px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
style="
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: #78747b;
|
||||
"
|
||||
>
|
||||
Have a great day! we hope you’ll have a great experience with us! <br />
|
||||
<br />
|
||||
Got a question? Get in touch with via 24/7 Live Chat or email us at help@purevpn.com.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 80px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="background: #fbfbfc">
|
||||
<tr>
|
||||
<td>
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
width="100%"
|
||||
style="background: #fbfbfc; max-width: 450px; margin: auto; padding: 0 15px"
|
||||
>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 30px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%">
|
||||
<td style="max-width: 50%">
|
||||
<img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_pure_btm_logo.png"
|
||||
alt="bottom_logo"
|
||||
/>
|
||||
</td>
|
||||
<td style="max-width: 50%">
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
width="100%"
|
||||
align="right"
|
||||
style="text-align: center"
|
||||
>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://www.facebook.com/PureVPNcom"
|
||||
><img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_fb_icon.png"
|
||||
alt="facebook"
|
||||
/></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://twitter.com/purevpn"
|
||||
><img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_twtr_icon.png"
|
||||
alt="twitter"
|
||||
/></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.linkedin.com/company/purevpn"
|
||||
><img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_lkndin_icon.png"
|
||||
alt="linked in"
|
||||
/></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.youtube.com/user/purevpn"
|
||||
><img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_ytube_icon.png"
|
||||
alt="youtube"
|
||||
/></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<hr style="border: 0; border-bottom: 1px solid #eae6ed" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #78747b; font-size: 11px">Download PureVPN on your:</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
<a
|
||||
href="https://purevpn-dialer-assets.s3.amazonaws.com/windows/app/purevpn_setup.exe"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>Windows</a
|
||||
>
|
||||
<a
|
||||
href="https://purevpn-dialer-assets.s3.amazonaws.com/mac/app/purevpn_setup.pkg"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>Mac</a
|
||||
>
|
||||
<a
|
||||
href="https://play.google.com/store/apps/details?id=com.gaditek.purevpnics&hl=en"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>Android</a
|
||||
>
|
||||
<a
|
||||
href="https://itunes.apple.com/us/app/purevpn-complete-online-privacy/id594506418?mt=8"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>iOS</a
|
||||
>
|
||||
<a
|
||||
href="https://chrome.google.com/webstore/detail/purevpn-free-vpn-proxy-un/bfidboloedlamgdmenmlbipfnccokknp"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>Chrome</a
|
||||
>
|
||||
<a
|
||||
href="https://addons.mozilla.org/en-US/firefox/addon/purevpn-for-privacy-security/"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
"
|
||||
>Firefox</a
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<hr style="border: 0; border-bottom: 1px solid #eae6ed" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 20px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #000; font-size: 11px; font-weight: bold">Got Questions?</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 5px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #78747b; font-size: 11px">
|
||||
Contact to our
|
||||
<a
|
||||
href="https://secure.livechatinc.com/licence/4454601/open_chat.cgi?utm_source=Expired&utm_medium=Email&utm_campaign=360%20is%20Coming%20Live%20Chat"
|
||||
style="color: #2c8fdc; text-decoration: none"
|
||||
>
|
||||
24x7 Live Chat!</a
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 15px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #78747b; font-size: 11px">
|
||||
@ 2007 - 2020 PureVPN All Rights Reserved
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 35px"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,236 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>${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: 14px !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%;
|
||||
}
|
||||
.apple-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;
|
||||
}
|
||||
.btn-primary table td:hover {
|
||||
background-color: #3498db !important;
|
||||
}
|
||||
.btn-primary a:hover {
|
||||
background-color: #3498db !important;
|
||||
border-color: #3498db !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body
|
||||
class=""
|
||||
style="
|
||||
background-color: #f6f6f6;
|
||||
font-family: sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
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: #f6f6f6;
|
||||
"
|
||||
>
|
||||
<tr>
|
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top"> </td>
|
||||
<td
|
||||
class="container"
|
||||
style="
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
vertical-align: top;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
max-width: 580px;
|
||||
padding: 10px;
|
||||
width: 580px;
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="content"
|
||||
style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 580px; padding: 10px"
|
||||
>
|
||||
<!-- 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;
|
||||
"
|
||||
>${preview}</span
|
||||
>
|
||||
<table
|
||||
class="main"
|
||||
style="
|
||||
border-collapse: separate;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
width: 100%;
|
||||
background: #ffffff;
|
||||
border-radius: 5px;
|
||||
"
|
||||
>
|
||||
<!-- START MAIN CONTENT AREA -->
|
||||
<tr>
|
||||
<td
|
||||
class="wrapper"
|
||||
style="
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
vertical-align: top;
|
||||
box-sizing: border-box;
|
||||
padding: 20px;
|
||||
"
|
||||
>
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="
|
||||
border-collapse: separate;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
width: 100%;
|
||||
"
|
||||
>
|
||||
<tr>
|
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top">
|
||||
${content}
|
||||
</td>
|
||||
</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: sans-serif;
|
||||
vertical-align: top;
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
font-size: 12px;
|
||||
color: #999999;
|
||||
text-align: center;
|
||||
"
|
||||
>
|
||||
<span
|
||||
class="apple-link"
|
||||
style="color: #999999; font-size: 12px; text-align: center"
|
||||
>${address}</span
|
||||
>
|
||||
<!--<br> Don't like these emails? <a href="" style="text-decoration: underline; color: #999999; font-size: 12px; text-align: center;">Unsubscribe</a>.-->
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!-- END FOOTER -->
|
||||
<!-- END CENTERED WHITE CONTAINER -->
|
||||
</div>
|
||||
</td>
|
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,7 @@
|
|||
<p>The following error occurred at {{ time }}:</p>
|
||||
|
||||
<p>
|
||||
Code: {{ code }} <br />
|
||||
Message: {{ message }} <br />
|
||||
Event ID: {{ eventId }}`
|
||||
</p>
|
|
@ -0,0 +1,356 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<!-- Cutom Font Embed CSS -->
|
||||
<link href="http://fonts.googleapis.com/css?family=Open+Sans:400,600,700" rel="stylesheet" type="text/css" />
|
||||
<title>Pure VPN</title>
|
||||
</head>
|
||||
<body
|
||||
leftmargin="0"
|
||||
marginwidth="0"
|
||||
topmargin="0"
|
||||
marginheight="0"
|
||||
offset="0"
|
||||
style="
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
background-color: #ffffff;
|
||||
height: 100% !important;
|
||||
width: 100% !important;
|
||||
"
|
||||
>
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
max-width: 600px;
|
||||
font-family: Verdana, Geneva, sans-serif;
|
||||
color: #2b2b2b;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
background: url('https://images.purevpn-tools.com/public/images/2791_eml_pur_bg_dull.png') #fff
|
||||
no-repeat top;
|
||||
background-size: contain;
|
||||
"
|
||||
>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 48px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center">
|
||||
<img src="https://images.purevpn-tools.com/public/images/2791_eml_pure_logo.png" alt="logo" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 52px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
width="100%"
|
||||
style="max-width: 450px; margin: auto; padding: 0 15px"
|
||||
>
|
||||
<tr>
|
||||
<td style="color: #000; font-size: 22px; font-weight: bold; line-height: 34px">
|
||||
Hi there!
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #78747b; font-size: 16px; font-weight: normal; line-height: 24px">
|
||||
Good news! {{ invitee }} has accepted your invite to join
|
||||
<strong>{{ orgName }}</strong>. Please confirm their membership to complete the process!
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 30px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ confirmMemberUrl }}"
|
||||
><button
|
||||
style="
|
||||
align-items: center;
|
||||
padding: 23px 50px;
|
||||
height: 60px;
|
||||
background: #28c675;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
line-height: 100%;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
"
|
||||
>
|
||||
Confirm Membership
|
||||
</button></a
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 40px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="border-bottom: 2px dashed #d9d3de"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 40px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
style="
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: #78747b;
|
||||
"
|
||||
>
|
||||
Have a great day! we hope you’ll have a great experience with us! <br />
|
||||
<br />
|
||||
Got a question? Get in touch with via 24/7 Live Chat or email us at help@purevpn.com.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 80px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="background: #fbfbfc">
|
||||
<tr>
|
||||
<td>
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
width="100%"
|
||||
style="background: #fbfbfc; max-width: 450px; margin: auto; padding: 0 15px"
|
||||
>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 30px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%">
|
||||
<td style="max-width: 50%">
|
||||
<img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_pure_btm_logo.png"
|
||||
alt="bottom_logo"
|
||||
/>
|
||||
</td>
|
||||
<td style="max-width: 50%">
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
width="100%"
|
||||
align="right"
|
||||
style="text-align: center"
|
||||
>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://www.facebook.com/PureVPNcom"
|
||||
><img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_fb_icon.png"
|
||||
alt="facebook"
|
||||
/></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://twitter.com/purevpn"
|
||||
><img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_twtr_icon.png"
|
||||
alt="twitter"
|
||||
/></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.linkedin.com/company/purevpn"
|
||||
><img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_lkndin_icon.png"
|
||||
alt="linked in"
|
||||
/></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.youtube.com/user/purevpn"
|
||||
><img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_ytube_icon.png"
|
||||
alt="youtube"
|
||||
/></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<hr style="border: 0; border-bottom: 1px solid #eae6ed" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #78747b; font-size: 11px">Download PureVPN on your:</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
<a
|
||||
href="https://purevpn-dialer-assets.s3.amazonaws.com/windows/app/purevpn_setup.exe"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>Windows</a
|
||||
>
|
||||
<a
|
||||
href="https://purevpn-dialer-assets.s3.amazonaws.com/mac/app/purevpn_setup.pkg"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>Mac</a
|
||||
>
|
||||
<a
|
||||
href="https://play.google.com/store/apps/details?id=com.gaditek.purevpnics&hl=en"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>Android</a
|
||||
>
|
||||
<a
|
||||
href="https://itunes.apple.com/us/app/purevpn-complete-online-privacy/id594506418?mt=8"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>iOS</a
|
||||
>
|
||||
<a
|
||||
href="https://chrome.google.com/webstore/detail/purevpn-free-vpn-proxy-un/bfidboloedlamgdmenmlbipfnccokknp"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>Chrome</a
|
||||
>
|
||||
<a
|
||||
href="https://addons.mozilla.org/en-US/firefox/addon/purevpn-for-privacy-security/"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
"
|
||||
>Firefox</a
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<hr style="border: 0; border-bottom: 1px solid #eae6ed" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 20px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #000; font-size: 11px; font-weight: bold">Got Questions?</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 5px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #78747b; font-size: 11px">
|
||||
Contact to our
|
||||
<a
|
||||
href="https://secure.livechatinc.com/licence/4454601/open_chat.cgi?utm_source=Expired&utm_medium=Email&utm_campaign=360%20is%20Coming%20Live%20Chat"
|
||||
style="color: #2c8fdc; text-decoration: none"
|
||||
>
|
||||
24x7 Live Chat!</a
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 15px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #78747b; font-size: 11px">
|
||||
@ 2007 - 2020 PureVPN All Rights Reserved
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 35px"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,356 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<!-- Cutom Font Embed CSS -->
|
||||
<link href="http://fonts.googleapis.com/css?family=Open+Sans:400,600,700" rel="stylesheet" type="text/css" />
|
||||
<title>Pure VPN</title>
|
||||
</head>
|
||||
<body
|
||||
leftmargin="0"
|
||||
marginwidth="0"
|
||||
topmargin="0"
|
||||
marginheight="0"
|
||||
offset="0"
|
||||
style="
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
background-color: #ffffff;
|
||||
height: 100% !important;
|
||||
width: 100% !important;
|
||||
"
|
||||
>
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
max-width: 600px;
|
||||
font-family: Verdana, Geneva, sans-serif;
|
||||
color: #2b2b2b;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
background: url('https://images.purevpn-tools.com/public/images/2791_eml_pur_bg_dull.png') #fff
|
||||
no-repeat top;
|
||||
background-size: contain;
|
||||
"
|
||||
>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 48px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center">
|
||||
<img src="https://images.purevpn-tools.com/public/images/2791_eml_pure_logo.png" alt="logo" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 52px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
width="100%"
|
||||
style="max-width: 450px; margin: auto; padding: 0 15px"
|
||||
>
|
||||
<tr>
|
||||
<td style="color: #000; font-size: 22px; font-weight: bold; line-height: 34px">
|
||||
Hi there!
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #78747b; font-size: 16px; font-weight: normal; line-height: 24px">
|
||||
Good news! You now have access to the PurePass organization
|
||||
<strong>{{ orgName }}</strong>.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 30px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ openAppUrl }}"
|
||||
><button
|
||||
style="
|
||||
align-items: center;
|
||||
padding: 23px 50px;
|
||||
height: 60px;
|
||||
background: #28c675;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
line-height: 100%;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
"
|
||||
>
|
||||
Open PurePass
|
||||
</button></a
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 40px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="border-bottom: 2px dashed #d9d3de"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 40px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
style="
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: #78747b;
|
||||
"
|
||||
>
|
||||
Have a great day! we hope you’ll have a great experience with us! <br />
|
||||
<br />
|
||||
Got a question? Get in touch with via 24/7 Live Chat or email us at help@purevpn.com.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 80px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="background: #fbfbfc">
|
||||
<tr>
|
||||
<td>
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
width="100%"
|
||||
style="background: #fbfbfc; max-width: 450px; margin: auto; padding: 0 15px"
|
||||
>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 30px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%">
|
||||
<td style="max-width: 50%">
|
||||
<img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_pure_btm_logo.png"
|
||||
alt="bottom_logo"
|
||||
/>
|
||||
</td>
|
||||
<td style="max-width: 50%">
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
width="100%"
|
||||
align="right"
|
||||
style="text-align: center"
|
||||
>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://www.facebook.com/PureVPNcom"
|
||||
><img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_fb_icon.png"
|
||||
alt="facebook"
|
||||
/></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://twitter.com/purevpn"
|
||||
><img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_twtr_icon.png"
|
||||
alt="twitter"
|
||||
/></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.linkedin.com/company/purevpn"
|
||||
><img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_lkndin_icon.png"
|
||||
alt="linked in"
|
||||
/></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.youtube.com/user/purevpn"
|
||||
><img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_ytube_icon.png"
|
||||
alt="youtube"
|
||||
/></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<hr style="border: 0; border-bottom: 1px solid #eae6ed" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #78747b; font-size: 11px">Download PureVPN on your:</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
<a
|
||||
href="https://purevpn-dialer-assets.s3.amazonaws.com/windows/app/purevpn_setup.exe"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>Windows</a
|
||||
>
|
||||
<a
|
||||
href="https://purevpn-dialer-assets.s3.amazonaws.com/mac/app/purevpn_setup.pkg"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>Mac</a
|
||||
>
|
||||
<a
|
||||
href="https://play.google.com/store/apps/details?id=com.gaditek.purevpnics&hl=en"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>Android</a
|
||||
>
|
||||
<a
|
||||
href="https://itunes.apple.com/us/app/purevpn-complete-online-privacy/id594506418?mt=8"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>iOS</a
|
||||
>
|
||||
<a
|
||||
href="https://chrome.google.com/webstore/detail/purevpn-free-vpn-proxy-un/bfidboloedlamgdmenmlbipfnccokknp"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>Chrome</a
|
||||
>
|
||||
<a
|
||||
href="https://addons.mozilla.org/en-US/firefox/addon/purevpn-for-privacy-security/"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
"
|
||||
>Firefox</a
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<hr style="border: 0; border-bottom: 1px solid #eae6ed" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 20px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #000; font-size: 11px; font-weight: bold">Got Questions?</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 5px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #78747b; font-size: 11px">
|
||||
Contact to our
|
||||
<a
|
||||
href="https://secure.livechatinc.com/licence/4454601/open_chat.cgi?utm_source=Expired&utm_medium=Email&utm_campaign=360%20is%20Coming%20Live%20Chat"
|
||||
style="color: #2c8fdc; text-decoration: none"
|
||||
>
|
||||
24x7 Live Chat!</a
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 15px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #78747b; font-size: 11px">
|
||||
@ 2007 - 2020 PureVPN All Rights Reserved
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 35px"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,356 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<!-- Cutom Font Embed CSS -->
|
||||
<link href="http://fonts.googleapis.com/css?family=Open+Sans:400,600,700" rel="stylesheet" type="text/css" />
|
||||
<title>Pure VPN</title>
|
||||
</head>
|
||||
<body
|
||||
leftmargin="0"
|
||||
marginwidth="0"
|
||||
topmargin="0"
|
||||
marginheight="0"
|
||||
offset="0"
|
||||
style="
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
background-color: #ffffff;
|
||||
height: 100% !important;
|
||||
width: 100% !important;
|
||||
"
|
||||
>
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
max-width: 600px;
|
||||
font-family: Verdana, Geneva, sans-serif;
|
||||
color: #2b2b2b;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
background: url('https://images.purevpn-tools.com/public/images/2791_eml_pur_bg_dull.png') #fff
|
||||
no-repeat top;
|
||||
background-size: contain;
|
||||
"
|
||||
>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 48px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center">
|
||||
<img src="https://images.purevpn-tools.com/public/images/2791_eml_pure_logo.png" alt="logo" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 52px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
width="100%"
|
||||
style="max-width: 450px; margin: auto; padding: 0 15px"
|
||||
>
|
||||
<tr>
|
||||
<td style="color: #000; font-size: 22px; font-weight: bold; line-height: 34px">
|
||||
Hi there!
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #78747b; font-size: 16px; font-weight: normal; line-height: 24px">
|
||||
You have been invited by {{ invitedBy }} to join their PurePass organization
|
||||
<strong>{{ orgName }}</strong>!
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 30px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ acceptInviteUrl }}"
|
||||
><button
|
||||
style="
|
||||
align-items: center;
|
||||
padding: 23px 50px;
|
||||
height: 60px;
|
||||
background: #28c675;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
line-height: 100%;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
"
|
||||
>
|
||||
Join {{ orgName }}
|
||||
</button></a
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 40px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="border-bottom: 2px dashed #d9d3de"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 40px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
style="
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: #78747b;
|
||||
"
|
||||
>
|
||||
Have a great day! we hope you’ll have a great experience with us! <br />
|
||||
<br />
|
||||
Got a question? Get in touch with via 24/7 Live Chat or email us at help@purevpn.com.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 80px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="background: #fbfbfc">
|
||||
<tr>
|
||||
<td>
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
width="100%"
|
||||
style="background: #fbfbfc; max-width: 450px; margin: auto; padding: 0 15px"
|
||||
>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 30px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%">
|
||||
<td style="max-width: 50%">
|
||||
<img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_pure_btm_logo.png"
|
||||
alt="bottom_logo"
|
||||
/>
|
||||
</td>
|
||||
<td style="max-width: 50%">
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
width="100%"
|
||||
align="right"
|
||||
style="text-align: center"
|
||||
>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://www.facebook.com/PureVPNcom"
|
||||
><img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_fb_icon.png"
|
||||
alt="facebook"
|
||||
/></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://twitter.com/purevpn"
|
||||
><img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_twtr_icon.png"
|
||||
alt="twitter"
|
||||
/></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.linkedin.com/company/purevpn"
|
||||
><img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_lkndin_icon.png"
|
||||
alt="linked in"
|
||||
/></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.youtube.com/user/purevpn"
|
||||
><img
|
||||
src="https://images.purevpn-tools.com/public/images/2791_eml_ytube_icon.png"
|
||||
alt="youtube"
|
||||
/></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<hr style="border: 0; border-bottom: 1px solid #eae6ed" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #78747b; font-size: 11px">Download PureVPN on your:</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
<a
|
||||
href="https://purevpn-dialer-assets.s3.amazonaws.com/windows/app/purevpn_setup.exe"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>Windows</a
|
||||
>
|
||||
<a
|
||||
href="https://purevpn-dialer-assets.s3.amazonaws.com/mac/app/purevpn_setup.pkg"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>Mac</a
|
||||
>
|
||||
<a
|
||||
href="https://play.google.com/store/apps/details?id=com.gaditek.purevpnics&hl=en"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>Android</a
|
||||
>
|
||||
<a
|
||||
href="https://itunes.apple.com/us/app/purevpn-complete-online-privacy/id594506418?mt=8"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>iOS</a
|
||||
>
|
||||
<a
|
||||
href="https://chrome.google.com/webstore/detail/purevpn-free-vpn-proxy-un/bfidboloedlamgdmenmlbipfnccokknp"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
border-right: 1px solid #78747b;
|
||||
padding-right: 5px;
|
||||
"
|
||||
>Chrome</a
|
||||
>
|
||||
<a
|
||||
href="https://addons.mozilla.org/en-US/firefox/addon/purevpn-for-privacy-security/"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: #2c8fdc;
|
||||
font-size: 11px;
|
||||
"
|
||||
>Firefox</a
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 10px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<hr style="border: 0; border-bottom: 1px solid #eae6ed" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 20px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #000; font-size: 11px; font-weight: bold">Got Questions?</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 5px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #78747b; font-size: 11px">
|
||||
Contact to our
|
||||
<a
|
||||
href="https://secure.livechatinc.com/licence/4454601/open_chat.cgi?utm_source=Expired&utm_medium=Email&utm_campaign=360%20is%20Coming%20Live%20Chat"
|
||||
style="color: #2c8fdc; text-decoration: none"
|
||||
>
|
||||
24x7 Live Chat!</a
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 15px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: #78747b; font-size: 11px">
|
||||
@ 2007 - 2020 PureVPN All Rights Reserved
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Spacing -->
|
||||
<td style="height: 35px"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -172,7 +172,7 @@ body {
|
|||
--start-background: var(--color-background-dark);
|
||||
--start-logo-height: 4em;
|
||||
--start-logo-width: auto;
|
||||
--start-form-shadow: rgb(0 0 0 / 10%) 0px 0px 2em -0.5em;
|
||||
--start-form-shadow: rgb(0 0 0 / 10%) 0px 0px 2em -1em;
|
||||
--start-form-background: var(--color-background);
|
||||
|
||||
/* MENU */
|
||||
|
|
|
@ -137,9 +137,8 @@ export class InviteRecipient extends Routing(StateMixin(LitElement)) {
|
|||
<div class="stretch large padded">${$l("Invite")}</div>
|
||||
|
||||
<div class="small tag ${status.class}">
|
||||
<pl-icon icon="${status.icon}"></pl-icon>
|
||||
|
||||
<div>${status.text}</div>
|
||||
<pl-icon icon="${status.icon}" class="inline"></pl-icon>
|
||||
${status.text}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
@ -156,7 +155,7 @@ export class InviteRecipient extends Routing(StateMixin(LitElement)) {
|
|||
|
||||
<div class="bold big margined centering layout">
|
||||
<div class="tag highlight">
|
||||
<pl-icon icon="members"></pl-icon>
|
||||
<pl-icon icon="members" class="large"></pl-icon>
|
||||
<div>${this._invite.org.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -169,9 +169,8 @@ export class InviteView extends Routing(StateMixin(LitElement)) {
|
|||
<div class="stretch large padded">${$l("Invite")}</div>
|
||||
|
||||
<div class="small tag ${status.class}">
|
||||
<pl-icon icon="${status.icon}"></pl-icon>
|
||||
|
||||
<div>${status.text}</div>
|
||||
<pl-icon icon="${status.icon}" class="inline"></pl-icon>
|
||||
${status.text}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ export class InviteView extends Routing(StateMixin(LitElement)) {
|
|||
<ptc-scroller class="stretch">
|
||||
<div class="tags">
|
||||
<div class="tag ${status.class}">
|
||||
<pl-icon icon="${status.icon}"></pl-icon>
|
||||
<pl-icon icon="${status.icon}" class="inline"></pl-icon>
|
||||
|
||||
<div>${status.text}</div>
|
||||
</div>
|
||||
|
|
|
@ -148,10 +148,10 @@ export class MemberView extends Routing(StateMixin(LitElement)) {
|
|||
vaults: [...this._vaults],
|
||||
groups: [...this._groups],
|
||||
});
|
||||
this._saveButton.success();
|
||||
this._saveButton?.success();
|
||||
this.requestUpdate();
|
||||
} catch (e) {
|
||||
this._saveButton.fail();
|
||||
this._saveButton?.fail();
|
||||
alert(e.message || $l("Something went wrong. Please try again later!"), { type: "warning" });
|
||||
throw e;
|
||||
}
|
||||
|
|
|
@ -95,6 +95,7 @@ export const reset = css`
|
|||
vertical-align: baseline;
|
||||
background: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
ol,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Auth, Authenticator, AuthenticatorStatus, AuthRequest, AuthServer, AuthType } from "../auth";
|
||||
import { Messenger } from "../messenger";
|
||||
import { EmailAuthMessage } from "../messages/mfa";
|
||||
import { EmailAuthMessage } from "../messenger";
|
||||
import { ErrorCode, Err } from "../error";
|
||||
import { randomNumber } from "../util";
|
||||
|
||||
|
@ -37,7 +37,7 @@ export class EmailAuthServer implements AuthServer {
|
|||
email,
|
||||
verificationCode,
|
||||
};
|
||||
this.messenger.send(authenticator.state.email, new EmailAuthMessage(verificationCode));
|
||||
this.messenger.send(authenticator.state.email, new EmailAuthMessage({ code: verificationCode }));
|
||||
return { email };
|
||||
}
|
||||
|
||||
|
|
|
@ -1,177 +0,0 @@
|
|||
export const fontFamily = "sans-serif";
|
||||
|
||||
export const fontSizeSmall = "12px";
|
||||
export const fontSize = "14px";
|
||||
export const fontSizeBig = "16px";
|
||||
|
||||
export const colorText = "#444444";
|
||||
export const colorHighlight = "#3498db";
|
||||
export const colorHover = "#3498db";
|
||||
export const colorBackground = "#f6f6f6";
|
||||
export const colorFooter = "#999999";
|
||||
|
||||
const footer = process.env.PL_EMAIL_FOOTER;
|
||||
|
||||
export function paragraph(content: string, styles = "") {
|
||||
return `
|
||||
<p style="font-family: ${fontFamily}; font-size: ${fontSize}; font-weight: normal; margin: 0; Margin-bottom: 15px; ${styles}">${content}</p>
|
||||
`;
|
||||
}
|
||||
|
||||
export function button(content: string, url: string) {
|
||||
return `
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-family: ${fontFamily}; font-size: ${fontSize}; vertical-align: top; padding-bottom: 15px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="font-family: ${fontFamily}; font-size: ${fontSize}; vertical-align: top; background-color: ${colorHighlight}; border-radius: 5px; text-align: center;"> <a href="${url}" target="_blank" style="display: inline-block; color: #ffffff; background-color: ${colorHighlight}; border: solid 1px ${colorHighlight}; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: ${fontSize}; font-weight: bold; margin: 0; padding: 12px 25px; text-transform: capitalize; border-color: ${colorHighlight};">${content}</a> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
}
|
||||
|
||||
export function base(content: string, preview = "", title = "") {
|
||||
return `
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>${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: ${fontSizeBig} !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%;
|
||||
}
|
||||
.apple-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;
|
||||
}
|
||||
.btn-primary table td:hover {
|
||||
background-color: ${colorHover} !important;
|
||||
}
|
||||
.btn-primary a:hover {
|
||||
background-color: ${colorHover} !important;
|
||||
border-color: ${colorHover} !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="" style="background-color: ${colorBackground}; font-family: ${fontFamily}; -webkit-font-smoothing: antialiased; font-size: ${fontSize}; line-height: 1.4; 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: ${colorBackground};">
|
||||
<tr>
|
||||
<td style="font-family: ${fontFamily}; font-size: ${fontSize}; vertical-align: top;"> </td>
|
||||
<td class="container" style="font-family: ${fontFamily}; font-size: ${fontSize}; vertical-align: top; display: block; Margin: 0 auto; max-width: 580px; padding: 10px; width: 580px;">
|
||||
<div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto; max-width: 580px; padding: 10px;">
|
||||
|
||||
<!-- 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;">${preview}</span>
|
||||
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #ffffff; border-radius: 5px;">
|
||||
|
||||
<!-- START MAIN CONTENT AREA -->
|
||||
<tr>
|
||||
<td class="wrapper" style="font-family: ${fontFamily}; font-size: ${fontSize}; vertical-align: top; box-sizing: border-box; padding: 20px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
|
||||
<tr>
|
||||
<td style="font-family: ${fontFamily}; font-size: ${fontSize}; vertical-align: top;">
|
||||
${content}
|
||||
</td>
|
||||
</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: ${fontFamily}; vertical-align: top; padding-bottom: 10px; padding-top: 10px; font-size: ${fontSizeSmall}; color: ${colorFooter}; text-align: center;">
|
||||
<span class="apple-link" style="color: ${colorFooter}; font-size: ${fontSizeSmall}; text-align: center;">${footer}</span>
|
||||
<!--<br> Don't like these emails? <a href="" style="text-decoration: underline; color: ${colorFooter}; font-size: ${fontSizeSmall}; text-align: center;">Unsubscribe</a>.-->
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!-- END FOOTER -->
|
||||
|
||||
<!-- END CENTERED WHITE CONTAINER -->
|
||||
</div>
|
||||
</td>
|
||||
<td style="font-family: ${fontFamily}; font-size: ${fontSize}; vertical-align: top;"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
// export { LoginMessage } from "./login";
|
||||
export { InviteCreatedMessage } from "./invite-created";
|
||||
export { InviteAcceptedMessage } from "./invite-accepted";
|
||||
export { MemberAddedMessage } from "./member-added";
|
||||
export { EmailAuthMessage } from "./mfa";
|
|
@ -1,45 +0,0 @@
|
|||
import { Invite } from "../invite";
|
||||
import { Message } from "../messenger";
|
||||
import { base as baseHTML, paragraph as p, button } from "./base-html";
|
||||
|
||||
export class InviteAcceptedMessage implements Message {
|
||||
constructor(private invite: Invite, private link: string) {}
|
||||
|
||||
get title() {
|
||||
return `${this.invite.invitee!.name || this.invite.email} has accepted your invite!`;
|
||||
}
|
||||
|
||||
get text() {
|
||||
const { invitee, org, email } = this.invite;
|
||||
return `
|
||||
Hi there!
|
||||
|
||||
Good news! ${invitee!.name || email} has accepted your invite to join ${org!.name}!
|
||||
Visit the following link to add them:
|
||||
|
||||
${this.link}
|
||||
|
||||
Have a great day!`;
|
||||
}
|
||||
|
||||
get html() {
|
||||
const { invitee, org, email } = this.invite;
|
||||
return baseHTML(
|
||||
`
|
||||
|
||||
${p("Hi there!")}
|
||||
|
||||
${p(`
|
||||
Good news! <strong>${invitee!.name || email}</strong> has
|
||||
accepted your invite to join <strong>${org!.name}</strong>!
|
||||
`)}
|
||||
|
||||
${button("Add Them Now", this.link)}
|
||||
|
||||
${p(`Have a great day!`)}
|
||||
`,
|
||||
this.title,
|
||||
this.title
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
import { Invite } from "../invite";
|
||||
import { Message } from "../messenger";
|
||||
import { base as baseHTML, paragraph as p, button } from "./base-html";
|
||||
|
||||
export class InviteCreatedMessage implements Message {
|
||||
constructor(public invite: Invite, public link: string) {}
|
||||
|
||||
get title() {
|
||||
const { org, invitedBy, purpose } = this.invite;
|
||||
return purpose === "confirm_membership"
|
||||
? `Confirm your membership for the "${org!.name}" org on Padloc!`
|
||||
: `${invitedBy!.name || invitedBy!.email} wants you to join the "${org!.name}" org on Padloc!`;
|
||||
}
|
||||
|
||||
get text() {
|
||||
const { org, invitedBy, purpose } = this.invite;
|
||||
return `
|
||||
Hi there!
|
||||
|
||||
${
|
||||
purpose === "confirm_membership"
|
||||
? `Please use the link below to reconfirm your membership for the "${org!.name}" org!`
|
||||
: `You have been invited by ${invitedBy!.name || invitedBy!.email} to join their org ` +
|
||||
`"${org!.name}" on Padloc! To accept the invite, please visit the link below:`
|
||||
}
|
||||
|
||||
${this.link}
|
||||
|
||||
Have a great day!`;
|
||||
}
|
||||
|
||||
get html() {
|
||||
const { org, invitedBy, purpose } = this.invite;
|
||||
return baseHTML(
|
||||
`
|
||||
|
||||
${p("Hi there!")}
|
||||
|
||||
${p(
|
||||
purpose === "confirm_membership"
|
||||
? `Please use the link below to reconfirm your membership for the ` +
|
||||
`<strong>${org!.name}</strong> org!`
|
||||
: `You have been invited by <strong>${invitedBy!.name || invitedBy!.email}</strong> ` +
|
||||
`to join their org <strong>${org!.name}</strong> on Padloc!
|
||||
`
|
||||
)}
|
||||
|
||||
${button(purpose === "confirm_membership" ? "Confirm Membership" : `Join ${org!.name}`, this.link)}
|
||||
|
||||
${p(`Have a great day!`)}
|
||||
`,
|
||||
this.title,
|
||||
this.title
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
import { Org } from "../org";
|
||||
import { Message } from "../messenger";
|
||||
import { base as baseHTML, paragraph as p, button } from "./base-html";
|
||||
|
||||
export class MemberAddedMessage implements Message {
|
||||
constructor(public org: Org, public link: string) {}
|
||||
|
||||
get title() {
|
||||
return `You have successfully joined ${this.org.name} on Padloc!`;
|
||||
}
|
||||
|
||||
get text() {
|
||||
const { name } = this.org;
|
||||
|
||||
return `
|
||||
Hi there!
|
||||
|
||||
You now have access to ${name} on Padloc! You can view it using the following link:
|
||||
|
||||
${this.link}
|
||||
|
||||
Have a great day`;
|
||||
}
|
||||
|
||||
get html() {
|
||||
const { name } = this.org;
|
||||
return baseHTML(
|
||||
`
|
||||
|
||||
${p("Hi there!")}
|
||||
|
||||
${p(`
|
||||
You now have access to <strong>${name}</strong> on Padloc!
|
||||
`)}
|
||||
|
||||
${button(`View ${name}`, this.link)}
|
||||
|
||||
${p(`Have a great day!`)}
|
||||
`,
|
||||
this.title,
|
||||
this.title
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
import { Message } from "../messenger";
|
||||
import { base as baseHTML, paragraph as p, colorBackground } from "./base-html";
|
||||
|
||||
export class EmailAuthMessage implements Message {
|
||||
constructor(public code: string) {}
|
||||
|
||||
title = "Verify Your Email Address";
|
||||
|
||||
get text() {
|
||||
return `
|
||||
Hi there!
|
||||
|
||||
Your email verifiation code is:
|
||||
|
||||
${this.code.toUpperCase()}
|
||||
|
||||
Have a great day!`;
|
||||
}
|
||||
|
||||
get html() {
|
||||
return baseHTML(
|
||||
`
|
||||
|
||||
${p("Hi there!")}
|
||||
|
||||
${p(`Your email verifiation code is:`)}
|
||||
|
||||
${p(
|
||||
this.code.toUpperCase(),
|
||||
`
|
||||
background-color: ${colorBackground};\
|
||||
padding: 15px;\
|
||||
border-radius: 10px;\
|
||||
font-size: 30px;\
|
||||
font-family: monospace;\
|
||||
text-align: center;\
|
||||
letter-spacing: 0.2em;\
|
||||
`
|
||||
)}
|
||||
|
||||
${p(`Have a great day!`)}
|
||||
`,
|
||||
this.title,
|
||||
this.title
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,25 +1,80 @@
|
|||
export type MessageData = { [param: string]: string };
|
||||
|
||||
/**
|
||||
* A message to be sent to a Padloc user
|
||||
* A message to be sent to a user
|
||||
*/
|
||||
export interface Message {
|
||||
export abstract class Message<T extends MessageData> {
|
||||
/** Message title */
|
||||
title: string;
|
||||
abstract get title(): string;
|
||||
|
||||
/** Message body, in plain text */
|
||||
text: string;
|
||||
/** Template name */
|
||||
abstract readonly template: string;
|
||||
|
||||
/** Message body, formated as html */
|
||||
html: string;
|
||||
constructor(public readonly data: T) {}
|
||||
}
|
||||
|
||||
export class EmailAuthMessage extends Message<{ code: string }> {
|
||||
template = "email-auth";
|
||||
|
||||
get title() {
|
||||
return "Verify Your Email Address";
|
||||
}
|
||||
}
|
||||
|
||||
abstract class OrgInviteMessage extends Message<{ orgName: string; invitedBy: string; acceptInviteUrl: string }> {}
|
||||
|
||||
export class JoinOrgInviteMessage extends OrgInviteMessage {
|
||||
template = "join-org-invite";
|
||||
|
||||
get title() {
|
||||
return `${this.data.invitedBy} wants you to join the "${this.data.orgName}" org on PurePass!`;
|
||||
}
|
||||
}
|
||||
|
||||
export class ConfirmMembershipInviteMessage extends OrgInviteMessage {
|
||||
template = "confirm-org-member-invite";
|
||||
|
||||
get title() {
|
||||
return `Confirm your membership for the "${this.data.orgName}" org on PurePass!`;
|
||||
}
|
||||
}
|
||||
|
||||
export class JoinOrgInviteAcceptedMessage extends Message<{
|
||||
orgName: string;
|
||||
invitee: string;
|
||||
confirmMemberUrl: string;
|
||||
}> {
|
||||
template = "join-org-invite-accepted";
|
||||
|
||||
get title() {
|
||||
return `${this.data.invitee} has accepted your invite!`;
|
||||
}
|
||||
}
|
||||
|
||||
export class JoinOrgInviteCompletedMessage extends Message<{ orgName: string; openAppUrl: string }> {
|
||||
template = "join-org-invite-completed";
|
||||
|
||||
get title() {
|
||||
return `You have successfully joined ${this.data.orgName} on PurePass!`;
|
||||
}
|
||||
}
|
||||
|
||||
export class ErrorMessage extends Message<{ code: string; message: string; time: string; eventId: string }> {
|
||||
template = "error";
|
||||
|
||||
get title() {
|
||||
return "Padloc Error Notification";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic interface for sending messages to Padloc users
|
||||
* Generic interface for sending messages to PurePass users
|
||||
*/
|
||||
export interface Messenger {
|
||||
/**
|
||||
* Sends a message to a given address
|
||||
*/
|
||||
send(addr: string, msg: Message): Promise<void>;
|
||||
send<T extends MessageData>(addr: string, msg: Message<T>): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,23 +86,23 @@ export class StubMessenger implements Messenger {
|
|||
* An array of messages passed to the [[send]] method. Sorted from
|
||||
* most recent to oldest.
|
||||
*/
|
||||
messages: { recipient: string; message: Message }[] = [];
|
||||
messages: { recipient: string; message: Message<any> }[] = [];
|
||||
|
||||
async send(recipient: string, message: Message) {
|
||||
async send<T extends MessageData>(recipient: string, message: Message<T>) {
|
||||
this.messages.unshift({ recipient, message });
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most recent message sent to `addr`.
|
||||
*/
|
||||
lastMessage(addr: string): Message | null {
|
||||
lastMessage(addr: string): Message<any> | null {
|
||||
const msg = this.messages.find(({ recipient }) => recipient === addr);
|
||||
return msg ? msg.message : null;
|
||||
}
|
||||
}
|
||||
|
||||
export class ConsoleMessenger implements Messenger {
|
||||
async send(recipient: string, message: Message) {
|
||||
console.log(`Message send to ${recipient}: ${message.text}`);
|
||||
async send(recipient: string, message: Message<any>) {
|
||||
console.log(`Message send to ${recipient}: ${message.data}`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ export class VaultQuota extends Serializable {
|
|||
}
|
||||
|
||||
items = -1;
|
||||
storage = -1;
|
||||
storage = 1000;
|
||||
}
|
||||
|
||||
export class OrgQuota extends Serializable {
|
||||
|
@ -29,9 +29,9 @@ export class OrgQuota extends Serializable {
|
|||
Object.assign(this, vals);
|
||||
}
|
||||
|
||||
members = -1;
|
||||
groups = -1;
|
||||
vaults = -1;
|
||||
members = 50;
|
||||
groups = 10;
|
||||
vaults = 10;
|
||||
}
|
||||
|
||||
export class AccountQuota extends Serializable {
|
||||
|
@ -41,7 +41,7 @@ export class AccountQuota extends Serializable {
|
|||
}
|
||||
|
||||
vaults = 1;
|
||||
orgs = -1;
|
||||
orgs = 3;
|
||||
|
||||
features: Feature[] = [];
|
||||
}
|
||||
|
|
|
@ -45,11 +45,17 @@ import { Err, ErrorCode } from "./error";
|
|||
import { Vault, VaultID } from "./vault";
|
||||
import { Org, OrgID, OrgRole } from "./org";
|
||||
import { Invite } from "./invite";
|
||||
import { Messenger } from "./messenger";
|
||||
import {
|
||||
ConfirmMembershipInviteMessage,
|
||||
ErrorMessage,
|
||||
JoinOrgInviteAcceptedMessage,
|
||||
JoinOrgInviteCompletedMessage,
|
||||
JoinOrgInviteMessage,
|
||||
Messenger,
|
||||
} from "./messenger";
|
||||
import { Server as SRPServer, SRPSession } from "./srp";
|
||||
import { DeviceInfo } from "./platform";
|
||||
import { getIdFromEmail, uuid } from "./util";
|
||||
import { InviteCreatedMessage, InviteAcceptedMessage, MemberAddedMessage } from "./messages";
|
||||
import { loadLanguage } from "@padloc/locale/src/translate";
|
||||
import { Logger } from "./log";
|
||||
import { PBES2Container } from "./container";
|
||||
|
@ -1075,18 +1081,24 @@ export class Controller extends API {
|
|||
signupRequest.status = AuthRequestStatus.Verified;
|
||||
auth.authRequests.push(signupRequest);
|
||||
params.set("next", path);
|
||||
params.set("mfaToken", signupRequest.token);
|
||||
params.set("mfaId", signupRequest.id);
|
||||
params.set("mfaVerified", "true");
|
||||
path = "signup/";
|
||||
params.set("authToken", signupRequest.token);
|
||||
params.set("email", invite.email);
|
||||
path = "signup/choose-password";
|
||||
}
|
||||
|
||||
await this.storage.save(auth);
|
||||
|
||||
const messageClass =
|
||||
invite.purpose === "confirm_membership" ? ConfirmMembershipInviteMessage : JoinOrgInviteMessage;
|
||||
|
||||
// Send invite link to invitees email address
|
||||
this.messenger.send(
|
||||
invite.email,
|
||||
new InviteCreatedMessage(invite, `${this.config.clientUrl}/${path}?${params.toString()}`)
|
||||
new messageClass({
|
||||
orgName: invite.org.name,
|
||||
invitedBy: invite.invitedBy!.name || invite.invitedBy!.email,
|
||||
acceptInviteUrl: `${this.config.clientUrl}/${path}?${params.toString()}`,
|
||||
})
|
||||
);
|
||||
})()
|
||||
);
|
||||
|
@ -1095,9 +1107,15 @@ export class Controller extends API {
|
|||
for (const invite of removedInvites) {
|
||||
promises.push(
|
||||
(async () => {
|
||||
const auth = await this._getAuth(invite.email);
|
||||
auth.invites = auth.invites.filter((inv) => inv.id !== invite.id);
|
||||
await this.storage.save(auth);
|
||||
try {
|
||||
const auth = await this._getAuth(invite.email);
|
||||
auth.invites = auth.invites.filter((inv) => inv.id !== invite.id);
|
||||
await this.storage.save(auth);
|
||||
} catch (e) {
|
||||
if (e.code !== ErrorCode.NOT_FOUND) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@ -1106,9 +1124,15 @@ export class Controller extends API {
|
|||
for (const { id } of removedMembers) {
|
||||
promises.push(
|
||||
(async () => {
|
||||
const acc = await this.storage.get(Account, id);
|
||||
acc.orgs = acc.orgs.filter((o) => o.id !== org.id);
|
||||
await this.storage.save(acc);
|
||||
try {
|
||||
const acc = await this.storage.get(Account, id);
|
||||
acc.orgs = acc.orgs.filter((o) => o.id !== org.id);
|
||||
await this.storage.save(acc);
|
||||
} catch (e) {
|
||||
if (e.code !== ErrorCode.NOT_FOUND) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
})()
|
||||
);
|
||||
}
|
||||
|
@ -1120,7 +1144,10 @@ export class Controller extends API {
|
|||
if (member.id !== account.id) {
|
||||
this.messenger.send(
|
||||
member.email,
|
||||
new MemberAddedMessage(org, `${this.config.clientUrl}/org/${org.id}`)
|
||||
new JoinOrgInviteCompletedMessage({
|
||||
orgName: org.name,
|
||||
openAppUrl: `${this.config.clientUrl}/org/${org.id}`,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1407,7 +1434,11 @@ export class Controller extends API {
|
|||
// the recipient has accepted the invite
|
||||
this.messenger.send(
|
||||
invite.invitedBy.email,
|
||||
new InviteAcceptedMessage(invite, `${this.config.clientUrl}/invite/${org.id}/${invite.id}`)
|
||||
new JoinOrgInviteAcceptedMessage({
|
||||
orgName: org.name,
|
||||
invitee: invite.invitee.name || invite.invitee.email,
|
||||
confirmMemberUrl: `${this.config.clientUrl}/invite/${org.id}/${invite.id}`,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1898,15 +1929,15 @@ export class Server {
|
|||
});
|
||||
|
||||
if (e.report && this.config.reportErrors) {
|
||||
this.messenger.send(this.config.reportErrors, {
|
||||
title: "Padloc Error Notification",
|
||||
text:
|
||||
`The following error occurred at ${e.time}:\n\n` +
|
||||
`Code: ${e.code}\n` +
|
||||
`Message: ${e.message}\n` +
|
||||
`Event ID: ${evt.id}`,
|
||||
html: "",
|
||||
});
|
||||
this.messenger.send(
|
||||
this.config.reportErrors,
|
||||
new ErrorMessage({
|
||||
time: e.time.toISOString(),
|
||||
code: e.code,
|
||||
message: e.message,
|
||||
eventId: evt.id,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import { Message, Messenger } from "@padloc/core/src/messenger";
|
||||
import { Message, MessageData, Messenger } from "@padloc/core/src/messenger";
|
||||
import { createTransport, Transporter, TransportOptions } from "nodemailer";
|
||||
import { Config, ConfigParam } from "@padloc/core/src/config";
|
||||
import { readFileSync, readdirSync } from "fs";
|
||||
import { Err, ErrorCode } from "@padloc/core/src/error";
|
||||
import { resolve } from "path";
|
||||
|
||||
export class SMTPConfig extends Config {
|
||||
constructor(init: Partial<SMTPConfig> = {}) {
|
||||
|
@ -23,38 +26,67 @@ export class SMTPConfig extends Config {
|
|||
@ConfigParam("string", true)
|
||||
password: string = "";
|
||||
|
||||
templateDir: string = "";
|
||||
|
||||
@ConfigParam()
|
||||
from?: string;
|
||||
}
|
||||
|
||||
export class SMTPSender implements Messenger {
|
||||
private transporter: Transporter;
|
||||
private _transporter: Transporter;
|
||||
|
||||
constructor(private opts: SMTPConfig) {
|
||||
private _templates = new Map<string, string>();
|
||||
|
||||
constructor(private config: SMTPConfig) {
|
||||
let auth = null;
|
||||
if (opts.user && opts.password) {
|
||||
if (config.user && config.password) {
|
||||
auth = {
|
||||
user: opts.user,
|
||||
pass: opts.password,
|
||||
user: config.user,
|
||||
pass: config.password,
|
||||
};
|
||||
}
|
||||
this.transporter = createTransport({
|
||||
host: opts.host,
|
||||
port: opts.port,
|
||||
secure: opts.secure,
|
||||
this._transporter = createTransport({
|
||||
host: config.host,
|
||||
port: config.port,
|
||||
secure: config.secure,
|
||||
auth: auth,
|
||||
} as TransportOptions);
|
||||
|
||||
this._loadTemplates(this.config.templateDir);
|
||||
}
|
||||
|
||||
send(email: string, message: Message) {
|
||||
private _loadTemplates(templateDir: string) {
|
||||
const files = readdirSync(templateDir);
|
||||
|
||||
for (const fileName of files) {
|
||||
this._templates.set(fileName.replace(/\.html$/, ""), readFileSync(resolve(templateDir, fileName), "utf-8"));
|
||||
}
|
||||
}
|
||||
|
||||
private _getMessageContent<T extends MessageData>(message: Message<T>) {
|
||||
let html = this._templates.get(message.template);
|
||||
if (!html) {
|
||||
throw new Err(ErrorCode.SERVER_ERROR, `Template not found: ${message.template}`);
|
||||
}
|
||||
|
||||
for (const [name, value] of Object.entries(message.data)) {
|
||||
html = html.replace(new RegExp(`{{ ?${name} ?}}`, "gi"), value);
|
||||
}
|
||||
|
||||
return { html };
|
||||
}
|
||||
|
||||
async send<T extends MessageData>(email: string, message: Message<T>) {
|
||||
const { html } = this._getMessageContent(message);
|
||||
|
||||
let opts = {
|
||||
from: this.opts.from || this.opts.user,
|
||||
from: this.config.from || this.config.user,
|
||||
to: email,
|
||||
subject: message.title,
|
||||
text: message.text,
|
||||
html: message.html,
|
||||
text: html,
|
||||
html,
|
||||
};
|
||||
|
||||
return this.transporter.sendMail(opts);
|
||||
return this._transporter.sendMail(opts);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import { TotpAuthConfig, TotpAuthServer } from "@padloc/core/src/auth/totp";
|
|||
import { EmailAuthServer } from "@padloc/core/src/auth/email";
|
||||
import { PublicKeyAuthServer } from "@padloc/core/src/auth/public-key";
|
||||
import { StripeProvisioner } from "./provisioning/stripe";
|
||||
import { resolve } from "path";
|
||||
|
||||
async function initDataStorage({ backend, leveldb, mongodb }: DataStorageConfig) {
|
||||
switch (backend) {
|
||||
|
@ -55,6 +56,9 @@ async function initLogger(config: LoggingConfig) {
|
|||
async function initEmailSender({ backend, smtp }: EmailConfig) {
|
||||
switch (backend) {
|
||||
case "smtp":
|
||||
if (!smtp!.templateDir) {
|
||||
smtp!.templateDir = resolve(process.env.PL_ASSETS_DIR || "../../assets", "email");
|
||||
}
|
||||
return new SMTPSender(smtp!);
|
||||
case "console":
|
||||
return new ConsoleMessenger();
|
||||
|
|
Loading…
Reference in New Issue