Merge pull request #1711 from Human-Connection/407-change_your_email_address

407 change your email address
This commit is contained in:
Robert Schäfer 2019-10-02 17:43:19 +02:00 committed by GitHub
commit cfdf9dad2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 2253 additions and 1448 deletions

View File

@ -5,7 +5,8 @@ import {
signupTemplate,
resetPasswordTemplate,
wrongAccountTemplate,
} from './templates/templateBuilder'
emailVerificationTemplate,
} from './templateBuilder'
const hasEmailConfig = CONFIG.SMTP_HOST && CONFIG.SMTP_PORT
const hasAuthData = CONFIG.SMTP_USERNAME && CONFIG.SMTP_PASSWORD
@ -57,8 +58,17 @@ const sendPasswordResetMail = async (resolve, root, args, context, resolveInfo)
return true
}
const sendEmailVerificationMail = async (resolve, root, args, context, resolveInfo) => {
const response = await resolve(root, args, context, resolveInfo)
const { email, nonce, name } = response
await sendMail(emailVerificationTemplate({ email, nonce, name }))
delete response.nonce
return response
}
export default {
Mutation: {
AddEmailAddress: sendEmailVerificationMail,
requestPasswordReset: sendPasswordResetMail,
Signup: sendSignupMail,
SignupByInvitation: sendSignupMail,

View File

@ -0,0 +1,77 @@
import mustache from 'mustache'
import CONFIG from '../../config'
import * as templates from './templates'
const from = '"Human Connection" <info@human-connection.org>'
const supportUrl = 'https://human-connection.org/en/contact'
export const signupTemplate = ({ email, nonce }) => {
const subject = 'Willkommen, Bienvenue, Welcome to Human Connection!'
const actionUrl = new URL('/registration/create-user-account', CONFIG.CLIENT_URI)
actionUrl.searchParams.set('nonce', nonce)
actionUrl.searchParams.set('email', email)
return {
from,
to: email,
subject,
html: mustache.render(
templates.layout,
{ actionUrl, supportUrl, subject },
{ content: templates.signup },
),
}
}
export const emailVerificationTemplate = ({ email, nonce, name }) => {
const subject = 'Neue E-Mail Adresse | New E-Mail Address'
const actionUrl = new URL('/settings/my-email-address/verify', CONFIG.CLIENT_URI)
actionUrl.searchParams.set('nonce', nonce)
actionUrl.searchParams.set('email', email)
return {
from,
to: email,
subject,
html: mustache.render(
templates.layout,
{ actionUrl, name, nonce, supportUrl, subject },
{ content: templates.emailVerification },
),
}
}
export const resetPasswordTemplate = ({ email, nonce, name }) => {
const subject = 'Neues Passwort | Reset Password'
const actionUrl = new URL('/password-reset/change-password', CONFIG.CLIENT_URI)
actionUrl.searchParams.set('nonce', nonce)
actionUrl.searchParams.set('email', email)
return {
from,
to: email,
subject,
html: mustache.render(
templates.layout,
{ actionUrl, name, nonce, supportUrl, subject },
{ content: templates.passwordReset },
),
}
}
export const wrongAccountTemplate = ({ email }) => {
const subject = 'Falsche Mailadresse? | Wrong E-mail?'
const actionUrl = new URL('/password-reset/request', CONFIG.CLIENT_URI)
return {
from,
to: email,
subject,
html: mustache.render(
templates.layout,
{ actionUrl, supportUrl },
{ content: templates.wrongAccount },
),
}
}

View File

@ -0,0 +1,190 @@
<!-- Email Body German : BEGIN -->
<table class="email-german" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
style="margin: auto;">
<tr>
<td style="padding: 20px 0; text-align: center">
</td>
</tr>
<!-- Hero Image, Flush : BEGIN -->
<tr>
<td style="background-color: #ffffff;">
<img
src="https://firebasestorage.googleapis.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LcGvGRsW6DrZn7FWRzF%2F-LcGv6EiVcsjYLfQ_2YE%2F-LcGv8UtmAWc61fxGveg%2Flets_get_together.png?generation=1555078880410873&alt=media"
width="600" height="" alt="Human Connection community logo" border="0"
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
class="g-img">
</td>
</tr>
<!-- Hero Image, Flush : END -->
<!-- 1 Column Text + Button : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Hallo {{ name }}!</h1>
<p style="margin: 0;">Du möchtest also deine E-Mail ändern? Kein Problem! Mit Klick auf diesen Button
kannst Du Deine neue E-Mail Adresse bestätigen:</p>
</td>
</tr>
<tr>
<td style="padding: 0 20px;">
<!-- Button : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin: auto;">
<tr>
<td class="button-td button-td-primary" style="border-radius: 4px; background: #17b53e;">
<a class="button-a button-a-primary" href="{{{ actionUrl }}}"
style="background: #17b53e; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;">E-Mail
Adresse
bestätigen</a>
</td>
</tr>
</table>
<!-- Button : END -->
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text + Button : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-bottom: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">Falls Du deine E-Mail Adresse doch nicht ändern möchtest, kannst du diese Nachricht
einfach ignorieren. Mlde Dich gerne <a href="{{{ supportUrl }}}" style="color: #17b53e;">bei
unserem Support Team</a>, wenn du noch Fragen hast!</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">Sollte der Button für Dich nicht funktionieren, kannst Du auch folgenden Code in
Dein Browserfenster kopieren: <span style="color: #17b53e;">{{{ nonce }}}</span></p>
<p style="margin: 0; margin-top: 10px;">Bis bald bei <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a>!</p>
<p style="margin: 0; margin-bottom: 10px;"> Dein Human Connection Team</p>
</td>
</tr>
<tr>
<td style="display: none;">
<p></p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
</table>
<!-- Email Body German : END -->
<!-- Email Body English : BEGIN -->
<table class="email-english" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
style="margin: auto;">
<tr>
<td style="padding: 20px 0; text-align: center">
</td>
</tr>
<!-- Hero Image, Flush : BEGIN -->
<tr>
<td style="background-color: #ffffff;">
<img
src="https://firebasestorage.googleapis.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LcGvGRsW6DrZn7FWRzF%2F-LcGv6EiVcsjYLfQ_2YE%2F-LcGv8UtmAWc61fxGveg%2Flets_get_together.png?generation=1555078880410873&alt=media"
width="600" height="" alt="Human Connection community logo" border="0"
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
class="g-img">
</td>
</tr>
<!-- Hero Image, Flush : END -->
<!-- 1 Column Text + Button : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Hello {{ name }}!</h1>
<p style="margin: 0;">So, you want to change your e-mail? No problem! Just click the button below to verify
your new address:</p>
</td>
</tr>
<tr>
<td style="padding: 0 20px;">
<!-- Button : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin: auto;">
<tr>
<td class="button-td button-td-primary" style="border-radius: 4px; background: #17b53e;">
<a class="button-a button-a-primary" href="{{{ actionUrl }}}"
style="background: #17b53e; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;">Verify
e-mail address</a>
</td>
</tr>
</table>
<!-- Button : END -->
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text + Button : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-bottom: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">If you don't want to change your e-mail address feel free to ignore this message. You
can
also <a href="{{{ supportUrl }}}" style="color: #17b53e;">contact our
support team</a> if you have any questions!</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">If the above button doesn't work you can also copy the following code into your
browser window: <span style="color: #17b53e;">{{{ nonce }}}</span></p>
<p style="margin: 0; margin-top: 10px;">See you soon on <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a>!</p>
<p style="margin: 0; margin-bottom: 10px;"> The Human Connection Team</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
</table>
<!-- Email Body English : END -->

View File

@ -0,0 +1,11 @@
import fs from 'fs'
import path from 'path'
const readFile = fileName => fs.readFileSync(path.join(__dirname, fileName), 'utf-8')
export const signup = readFile('./signup.html')
export const passwordReset = readFile('./resetPassword.html')
export const wrongAccount = readFile('./wrongAccount.html')
export const emailVerification = readFile('./emailVerification.html')
export const layout = readFile('./layout.html')

View File

@ -0,0 +1,256 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="x-apple-disable-message-reformatting">
<meta name="format-detection" content="telephone=no,address=no,email=no,date=no,url=no">
<title>{{ subject }}</title>
<!--[if mso]>
<style>
* {
font-family: sans-serif !important;
}
</style>
<![endif]-->
<!--[if !mso]><!-->
<link href='https://fonts.googleapis.com/css?family=Lato:400,700' rel='stylesheet' type='text/css'>
<!--<![endif]-->
<!-- CSS RESETS -->
<style>
html,
body {
margin: 0 !important;
padding: 0 !important;
height: 100% !important;
width: 100% !important;
}
* {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
div[style*="margin: 16px 0"] {
margin: 0 !important;
}
table,
td {
mso-table-lspace: 0pt !important;
mso-table-rspace: 0pt !important;
}
table {
border-spacing: 0 !important;
border-collapse: collapse !important;
table-layout: fixed !important;
margin: 0 auto !important;
}
img {
-ms-interpolation-mode: bicubic;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a[x-apple-data-detectors],
.unstyle-auto-detected-links a,
.aBn {
border-bottom: 0 !important;
cursor: default !important;
color: inherit !important;
text-decoration: none !important;
font-size: inherit !important;
font-family: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
}
.a6S {
display: none !important;
opacity: 0.01 !important;
}
.im {
color: inherit !important;
}
img.g-img+div {
display: none !important;
}
/* iPhone 4, 4S, 5, 5S, 5C, and 5SE */
@media only screen and (min-device-width: 320px) and (max-device-width: 374px) {
u~div .email-container {
min-width: 320px !important;
}
}
/* iPhone 6, 6S, 7, 8, and X */
@media only screen and (min-device-width: 375px) and (max-device-width: 413px) {
u~div .email-container {
min-width: 375px !important;
}
}
/* iPhone 6+, 7+, and 8+ */
@media only screen and (min-device-width: 414px) {
u~div .email-container {
min-width: 414px !important;
}
}
</style>
<!--[if gte mso 9]>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
<![endif]-->
<!-- PROGRESSIVE ENHANCEMENTS -->
<style>
.button-td,
.button-a {
transition: all 100ms ease-in;
}
.button-td-primary:hover,
.button-a-primary:hover {
background: #19c243 !important;
border-color: #555555 !important;
}
@media screen and (max-width: 600px) {
.email-container p {
font-size: 17px !important;
}
}
</style>
<!-- LANGUAGE TOGGLE -->
<style>
.toggle+label {
display: inline-block;
height: 40px;
padding: 0 12px;
margin-top: 40px;
line-height: 38px;
font-family: Lato, sans-serif;
font-size: 16px;
border: 1px solid #cbc7d1;
cursor: pointer;
}
.toggle+label:hover {
background-color: #bee876;
}
.toggle:checked+label {
background-color: #19c243;
color: white;
}
.toggle-english+label {
border-bottom-right-radius: 50px;
border-top-right-radius: 50px;
border-left: none;
margin-left: -2px;
}
.toggle-german+label {
border-bottom-left-radius: 50px;
border-top-left-radius: 50px;
border-right: none;
margin-right: -2px;
}
.toggle-german:checked~table.email-german {
display: block;
}
.toggle-german:checked~table.email-english {
display: none;
}
.toggle-english:checked~table.email-english {
display: block;
}
.toggle-english:checked~table.email-german {
display: none;
}
</style>
</head>
<body width="100%" style="margin: 0; padding: 0 !important; mso-line-height-rule: exactly; background-color: #f5f4f6;">
<center style="width: 100%; background-color: #f5f4f6;">
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: #f5f4f6;">
<tr>
<td>
<![endif]-->
<div style="max-width: 600px; margin: 0 auto;" class="email-container">
<!--[if mso]>
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="600">
<tr>
<td>
<![endif]-->
<!-- LANGUAGE TOGGLE -->
<input type="radio" name="language" class="toggle toggle-german" style="display: none;" id="toggle-german"
checked="checked">
<label for="toggle-german">Deutsch</label>
<input type="radio" name="language" class="toggle toggle-english" style="display:none;" id="toggle-english">
<label for="toggle-english">English</label>
<p style="margin: 0;"></p>
{{> content}}
<!-- Email Footer : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
style="margin: auto;">
<tr>
<td
style="padding: 20px; font-family: Lato, sans-serif; font-size: 12px; line-height: 15px; text-align: center; color: #888888;">
<br><br>
Human Connection gGmbH<br><span class="unstyle-auto-detected-links">Bahnhofstraße 11, 73235 Weilheim /
Teck<br>Germany</span>
<br><br>
</td>
</tr>
</table>
<!-- Email Footer : END -->
<!--[if mso]>
</td>
</tr>
</table>
<![endif]-->
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<![endif]-->
</center>
</body>
</html>

View File

@ -1,448 +1,189 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:o="urn:schemas-microsoft-com:office:office">
<!-- Email Body German : BEGIN -->
<table class="email-german" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
style="margin: auto;">
<tr>
<td style="padding: 20px 0; text-align: center">
</td>
</tr>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="x-apple-disable-message-reformatting">
<meta name="format-detection" content="telephone=no,address=no,email=no,date=no,url=no">
<title>Neues Passwort | Reset Password</title>
<!-- Hero Image, Flush : BEGIN -->
<tr>
<td style="background-color: #ffffff;">
<img
src="https://firebasestorage.googleapis.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LcGvGRsW6DrZn7FWRzF%2F-LcGv6EiVcsjYLfQ_2YE%2F-LcGv8UtmAWc61fxGveg%2Flets_get_together.png?generation=1555078880410873&alt=media"
width="600" height="" alt="Human Connection community logo" border="0"
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
class="g-img">
</td>
</tr>
<!-- Hero Image, Flush : END -->
<!--[if mso]>
<style>
* {
font-family: sans-serif !important;
}
</style>
<![endif]-->
<!--[if !mso]><!-->
<link href='https://fonts.googleapis.com/css?family=Lato:400,700' rel='stylesheet' type='text/css'>
<!--<![endif]-->
<!-- CSS RESETS -->
<style>
html,
body {
margin: 0 !important;
padding: 0 !important;
height: 100% !important;
width: 100% !important;
}
* {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
div[style*="margin: 16px 0"] {
margin: 0 !important;
}
table,
td {
mso-table-lspace: 0pt !important;
mso-table-rspace: 0pt !important;
}
table {
border-spacing: 0 !important;
border-collapse: collapse !important;
table-layout: fixed !important;
margin: 0 auto !important;
}
img {
-ms-interpolation-mode: bicubic;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a[x-apple-data-detectors],
.unstyle-auto-detected-links a,
.aBn {
border-bottom: 0 !important;
cursor: default !important;
color: inherit !important;
text-decoration: none !important;
font-size: inherit !important;
font-family: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
}
.a6S {
display: none !important;
opacity: 0.01 !important;
}
.im {
color: inherit !important;
}
img.g-img+div {
display: none !important;
}
/* iPhone 4, 4S, 5, 5S, 5C, and 5SE */
@media only screen and (min-device-width: 320px) and (max-device-width: 374px) {
u~div .email-container {
min-width: 320px !important;
}
}
/* iPhone 6, 6S, 7, 8, and X */
@media only screen and (min-device-width: 375px) and (max-device-width: 413px) {
u~div .email-container {
min-width: 375px !important;
}
}
/* iPhone 6+, 7+, and 8+ */
@media only screen and (min-device-width: 414px) {
u~div .email-container {
min-width: 414px !important;
}
}
</style>
<!--[if gte mso 9]>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
<![endif]-->
<!-- PROGRESSIVE ENHANCEMENTS -->
<style>
.button-td,
.button-a {
transition: all 100ms ease-in;
}
.button-td-primary:hover,
.button-a-primary:hover {
background: #19c243 !important;
border-color: #555555 !important;
}
@media screen and (max-width: 600px) {
.email-container p {
font-size: 17px !important;
}
}
</style>
<!-- LANGUAGE TOGGLE -->
<style>
.toggle+label {
display: inline-block;
height: 40px;
padding: 0 12px;
margin-top: 40px;
line-height: 38px;
font-family: Lato, sans-serif;
font-size: 16px;
border: 1px solid #cbc7d1;
cursor: pointer;
}
.toggle+label:hover {
background-color: #bee876;
}
.toggle:checked+label {
background-color: #19c243;
color: white;
}
.toggle-english+label {
border-bottom-right-radius: 50px;
border-top-right-radius: 50px;
border-left: none;
margin-left: -2px;
}
.toggle-german+label {
border-bottom-left-radius: 50px;
border-top-left-radius: 50px;
border-right: none;
margin-right: -2px;
}
.toggle-german:checked~table.email-german {
display: block;
}
.toggle-german:checked~table.email-english {
display: none;
}
.toggle-english:checked~table.email-english {
display: block;
}
.toggle-english:checked~table.email-german {
display: none;
}
</style>
</head>
<body width="100%" style="margin: 0; padding: 0 !important; mso-line-height-rule: exactly; background-color: #f5f4f6;">
<center style="width: 100%; background-color: #f5f4f6;">
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: #f5f4f6;">
<tr>
<td>
<![endif]-->
<div style="max-width: 600px; margin: 0 auto;" class="email-container">
<!--[if mso]>
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="600">
<tr>
<td>
<![endif]-->
<!-- LANGUAGE TOGGLE -->
<input type="radio" name="language" class="toggle toggle-german" style="display: none;" id="toggle-german"
checked="checked">
<label for="toggle-german">Deutsch</label>
<input type="radio" name="language" class="toggle toggle-english" style="display:none;" id="toggle-english">
<label for="toggle-english">English</label>
<p style="margin: 0;"></p>
<!-- Email Body German : BEGIN -->
<table class="email-german" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0"
width="100%" style="margin: auto;">
<tr>
<td style="padding: 20px 0; text-align: center">
</td>
</tr>
<!-- Hero Image, Flush : BEGIN -->
<tr>
<td style="background-color: #ffffff;">
<img
src="https://firebasestorage.googleapis.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LcGvGRsW6DrZn7FWRzF%2F-LcGv6EiVcsjYLfQ_2YE%2F-LcGv8UtmAWc61fxGveg%2Flets_get_together.png?generation=1555078880410873&alt=media"
width="600" height="" alt="Human Connection community logo" border="0"
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
class="g-img">
</td>
</tr>
<!-- Hero Image, Flush : END -->
<!-- 1 Column Text + Button : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Hallo {{ name }}!</h1>
<p style="margin: 0;">Du hast also dein Passwort vergessen? Kein Problem! Mit Klick auf diesen Button
kannst Du innerhalb der nächsten 24 Stunden Dein Passwort zurücksetzen:</p>
</td>
</tr>
<tr>
<td style="padding: 0 20px;">
<!-- Button : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0"
style="margin: auto;">
<tr>
<td class="button-td button-td-primary" style="border-radius: 4px; background: #17b53e;">
<a class="button-a button-a-primary" href="{{{ actionUrl }}}"
style="background: #17b53e; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;">Passwort
zurücksetzen</a>
</td>
</tr>
</table>
<!-- Button : END -->
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text + Button : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-bottom: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">Falls Du kein neues Passwort angefordert hast, kannst Du diese E-Mail einfach
ignorieren. Wenn Du noch Fragen hast, melde Dich gerne <a href="{{{ supportUrl }}}"
style="color: #17b53e;">bei
unserem Support Team</a>!</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">Sollte der Button für Dich nicht funktionieren, kannst Du auch folgenden Code in
Dein Browserfenster kopieren: <span style="color: #17b53e;">{{{ nonce }}}</span></p>
<p style="margin: 0; margin-top: 10px;">Bis bald bei <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a>!</p>
<p style="margin: 0; margin-bottom: 10px;"> Dein Human Connection Team</p>
</td>
</tr>
<tr>
<td style="display: none;">
<p></p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
</table>
<!-- Email Body German : END -->
<!-- Email Body English : BEGIN -->
<table class="email-english" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0"
width="100%" style="margin: auto;">
<tr>
<td style="padding: 20px 0; text-align: center">
</td>
</tr>
<!-- Hero Image, Flush : BEGIN -->
<tr>
<td style="background-color: #ffffff;">
<img
src="https://firebasestorage.googleapis.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LcGvGRsW6DrZn7FWRzF%2F-LcGv6EiVcsjYLfQ_2YE%2F-LcGv8UtmAWc61fxGveg%2Flets_get_together.png?generation=1555078880410873&alt=media"
width="600" height="" alt="Human Connection community logo" border="0"
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
class="g-img">
</td>
</tr>
<!-- Hero Image, Flush : END -->
<!-- 1 Column Text + Button : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Hello {{ name }}!</h1>
<p style="margin: 0;">So, you forgot your password? No problem! Just click the button below to reset
it within the next 24 hours:</p>
</td>
</tr>
<tr>
<td style="padding: 0 20px;">
<!-- Button : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0"
style="margin: auto;">
<tr>
<td class="button-td button-td-primary" style="border-radius: 4px; background: #17b53e;">
<a class="button-a button-a-primary" href="{{{ actionUrl }}}"
style="background: #17b53e; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;">Reset
password</a>
</td>
</tr>
</table>
<!-- Button : END -->
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text + Button : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-bottom: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">If you didn't request a new password feel free to ignore this e-mail. You can
also <a href="{{{ supportUrl }}}" style="color: #17b53e;">contact our
support team</a> if you have any questions!</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">If the above button doesn't work you can also copy the following code into your
browser window: <span style="color: #17b53e;">{{{ nonce }}}</span></p>
<p style="margin: 0; margin-top: 10px;">See you soon on <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a>!</p>
<p style="margin: 0; margin-bottom: 10px;"> The Human Connection Team</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
</table>
<!-- Email Body English : END -->
<!-- Email Footer : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
style="margin: auto;">
<!-- 1 Column Text + Button : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; font-family: Lato, sans-serif; font-size: 12px; line-height: 15px; text-align: center; color: #888888;">
<br><br>
Human Connection gGmbH<br><span class="unstyle-auto-detected-links">Bahnhofstraße 11, 73235 Weilheim /
Teck<br>Germany</span>
<br><br>
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Hallo {{ name }}!</h1>
<p style="margin: 0;">Du hast also dein Passwort vergessen? Kein Problem! Mit Klick auf diesen Button
kannst Du innerhalb der nächsten 24 Stunden Dein Passwort zurücksetzen:</p>
</td>
</tr>
<tr>
<td style="padding: 0 20px;">
<!-- Button : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin: auto;">
<tr>
<td class="button-td button-td-primary" style="border-radius: 4px; background: #17b53e;">
<a class="button-a button-a-primary" href="{{{ actionUrl }}}"
style="background: #17b53e; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;">Passwort
zurücksetzen</a>
</td>
</tr>
</table>
<!-- Button : END -->
</td>
</tr>
</table>
<!-- Email Footer : END -->
</td>
</tr>
<!-- 1 Column Text + Button : END -->
<!--[if mso]>
</td>
</tr>
</table>
<![endif]-->
</div>
<!--[if mso | IE]>
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-bottom: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">Falls Du kein neues Passwort angefordert hast, kannst Du diese E-Mail einfach
ignorieren. Wenn Du noch Fragen hast, melde Dich gerne <a href="{{{ supportUrl }}}"
style="color: #17b53e;">bei
unserem Support Team</a>!</p>
</td>
</tr>
</table>
<![endif]-->
</center>
</body>
</td>
</tr>
<!-- 1 Column Text : END -->
</html>
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">Sollte der Button für Dich nicht funktionieren, kannst Du auch folgenden Code in
Dein Browserfenster kopieren: <span style="color: #17b53e;">{{{ nonce }}}</span></p>
<p style="margin: 0; margin-top: 10px;">Bis bald bei <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a>!</p>
<p style="margin: 0; margin-bottom: 10px;"> Dein Human Connection Team</p>
</td>
</tr>
<tr>
<td style="display: none;">
<p></p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
</table>
<!-- Email Body German : END -->
<!-- Email Body English : BEGIN -->
<table class="email-english" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
style="margin: auto;">
<tr>
<td style="padding: 20px 0; text-align: center">
</td>
</tr>
<!-- Hero Image, Flush : BEGIN -->
<tr>
<td style="background-color: #ffffff;">
<img
src="https://firebasestorage.googleapis.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LcGvGRsW6DrZn7FWRzF%2F-LcGv6EiVcsjYLfQ_2YE%2F-LcGv8UtmAWc61fxGveg%2Flets_get_together.png?generation=1555078880410873&alt=media"
width="600" height="" alt="Human Connection community logo" border="0"
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
class="g-img">
</td>
</tr>
<!-- Hero Image, Flush : END -->
<!-- 1 Column Text + Button : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Hello {{ name }}!</h1>
<p style="margin: 0;">So, you forgot your password? No problem! Just click the button below to reset
it within the next 24 hours:</p>
</td>
</tr>
<tr>
<td style="padding: 0 20px;">
<!-- Button : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin: auto;">
<tr>
<td class="button-td button-td-primary" style="border-radius: 4px; background: #17b53e;">
<a class="button-a button-a-primary" href="{{{ actionUrl }}}"
style="background: #17b53e; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;">Reset
password</a>
</td>
</tr>
</table>
<!-- Button : END -->
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text + Button : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-bottom: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">If you didn't request a new password feel free to ignore this e-mail. You can
also <a href="{{{ supportUrl }}}" style="color: #17b53e;">contact our
support team</a> if you have any questions!</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">If the above button doesn't work you can also copy the following code into your
browser window: <span style="color: #17b53e;">{{{ nonce }}}</span></p>
<p style="margin: 0; margin-top: 10px;">See you soon on <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a>!</p>
<p style="margin: 0; margin-bottom: 10px;"> The Human Connection Team</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
</table>
<!-- Email Body English : END -->

View File

@ -1,485 +1,214 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:o="urn:schemas-microsoft-com:office:office">
<!-- Email Body German : BEGIN -->
<table class="email-german" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
style="margin: auto;">
<tr>
<td style="padding: 20px 0; text-align: center">
</td>
</tr>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="x-apple-disable-message-reformatting">
<meta name="format-detection" content="telephone=no,address=no,email=no,date=no,url=no">
<title>Willkommen, Bienvenue, Welcome to Human Connection</title>
<!-- Hero Image, Flush : BEGIN -->
<tr>
<td style="background-color: #ffffff;">
<img
src="https://firebasestorage.googleapis.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LcGvGRsW6DrZn7FWRzF%2F-LcGv6EiVcsjYLfQ_2YE%2F-LcGv8UtmAWc61fxGveg%2Flets_get_together.png?generation=1555078880410873&alt=media"
width="600" height="" alt="Human Connection community logo" border="0"
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
class="g-img">
</td>
</tr>
<!-- Hero Image, Flush : END -->
<!--[if mso]>
<style>
* {
font-family: sans-serif !important;
}
</style>
<![endif]-->
<!--[if !mso]><!-->
<link href='https://fonts.googleapis.com/css?family=Lato:400,700' rel='stylesheet' type='text/css'>
<!--<![endif]-->
<!-- CSS RESETS -->
<style>
html,
body {
margin: 0 !important;
padding: 0 !important;
height: 100% !important;
width: 100% !important;
}
* {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
div[style*="margin: 16px 0"] {
margin: 0 !important;
}
table,
td {
mso-table-lspace: 0pt !important;
mso-table-rspace: 0pt !important;
}
table {
border-spacing: 0 !important;
border-collapse: collapse !important;
table-layout: fixed !important;
margin: 0 auto !important;
}
img {
-ms-interpolation-mode: bicubic;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a[x-apple-data-detectors],
.unstyle-auto-detected-links a,
.aBn {
border-bottom: 0 !important;
cursor: default !important;
color: inherit !important;
text-decoration: none !important;
font-size: inherit !important;
font-family: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
}
.a6S {
display: none !important;
opacity: 0.01 !important;
}
.im {
color: inherit !important;
}
img.g-img+div {
display: none !important;
}
/* iPhone 4, 4S, 5, 5S, 5C, and 5SE */
@media only screen and (min-device-width: 320px) and (max-device-width: 374px) {
u~div .email-container {
min-width: 320px !important;
}
}
/* iPhone 6, 6S, 7, 8, and X */
@media only screen and (min-device-width: 375px) and (max-device-width: 413px) {
u~div .email-container {
min-width: 375px !important;
}
}
/* iPhone 6+, 7+, and 8+ */
@media only screen and (min-device-width: 414px) {
u~div .email-container {
min-width: 414px !important;
}
}
</style>
<!--[if gte mso 9]>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
<![endif]-->
<!-- PROGRESSIVE ENHANCEMENTS -->
<style>
.button-td,
.button-a {
transition: all 100ms ease-in;
}
.button-td-primary:hover,
.button-a-primary:hover {
background: #19c243 !important;
border-color: #555555 !important;
}
@media screen and (max-width: 600px) {
.email-container p {
font-size: 17px !important;
}
}
</style>
<!-- LANGUAGE TOGGLE -->
<style>
.toggle+label {
display: inline-block;
height: 40px;
padding: 0 12px;
margin-top: 40px;
line-height: 38px;
font-family: Lato, sans-serif;
font-size: 16px;
border: 1px solid #cbc7d1;
cursor: pointer;
}
.toggle+label:hover {
background-color: #bee876;
}
.toggle:checked+label {
background-color: #19c243;
color: white;
}
.toggle-english+label {
border-bottom-right-radius: 50px;
border-top-right-radius: 50px;
border-left: none;
margin-left: -2px;
}
.toggle-german+label {
border-bottom-left-radius: 50px;
border-top-left-radius: 50px;
border-right: none;
margin-right: -2px;
}
.toggle-german:checked~table.email-german {
display: block;
}
.toggle-german:checked~table.email-english {
display: none;
}
.toggle-english:checked~table.email-english {
display: block;
}
.toggle-english:checked~table.email-german {
display: none;
}
</style>
</head>
<body width="100%" style="margin: 0; padding: 0 !important; mso-line-height-rule: exactly; background-color: #f5f4f6;">
<center style="width: 100%; background-color: #f5f4f6;">
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: #f5f4f6;">
<tr>
<td>
<![endif]-->
<!-- VISUALLY HIDDEN PRE-HEADER TEXT -->
<div
style="display: none; font-size: 1px; line-height: 1px; max-height: 0px; max-width: 0px; opacity: 0; overflow: hidden; mso-hide: all; font-family: sans-serif;">
Dein Anmeldelink. | Here is your signup link.
</div>
<div
style="display: none; font-size: 1px; line-height: 1px; max-height: 0px; max-width: 0px; opacity: 0; overflow: hidden; mso-hide: all; font-family: sans-serif;">
&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;<br>
</div>
<div style="max-width: 600px; margin: 0 auto;" class="email-container">
<!--[if mso]>
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="600">
<tr>
<td>
<![endif]-->
<!-- LANGUAGE TOGGLE -->
<input type="radio" name="language" class="toggle toggle-german" style="display: none;" id="toggle-german"
checked="checked">
<label for="toggle-german">Deutsch</label>
<input type="radio" name="language" class="toggle toggle-english" style="display:none;" id="toggle-english">
<label for="toggle-english">English</label>
<p style="margin: 0;"></p>
<!-- Email Body German : BEGIN -->
<table class="email-german" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0"
width="100%" style="margin: auto;">
<tr>
<td style="padding: 20px 0; text-align: center">
</td>
</tr>
<!-- Hero Image, Flush : BEGIN -->
<tr>
<td style="background-color: #ffffff;">
<img
src="https://firebasestorage.googleapis.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LcGvGRsW6DrZn7FWRzF%2F-LcGv6EiVcsjYLfQ_2YE%2F-LcGv8UtmAWc61fxGveg%2Flets_get_together.png?generation=1555078880410873&alt=media"
width="600" height="" alt="Human Connection community logo" border="0"
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
class="g-img">
</td>
</tr>
<!-- Hero Image, Flush : END -->
<!-- 1 Column Text + Button : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Willkommen bei Human Connection!</h1>
<p style="margin: 0;">Danke, dass Du dich angemeldet hast wir freuen uns, Dich dabei zu haben. Jetzt
fehlt nur noch eine Kleinigkeit, bevor wir gemeinsam die Welt verbessern können ... Bitte bestätige
Deine E-Mail Adresse:</p>
</td>
</tr>
<tr>
<td style="padding: 0 20px;">
<!-- Button : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0"
style="margin: auto;">
<tr>
<td class="button-td button-td-primary" style="border-radius: 4px; background: #17b53e;">
<a class="button-a button-a-primary" href="{{{ actionUrl }}}"
style="background: #17b53e; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;">Bestätige
Deine E-Mail Adresse</a>
</td>
</tr>
</table>
<!-- Button : END -->
</td>
</tr>
<tr>
<td style="text-align: center; color :#17b53e">
<p></p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text + Button : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">Falls Du Dich nicht selbst bei <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a> angemeldet hast, schau doch mal vorbei!
Wir sind ein gemeinnütziges Aktionsnetzwerk von Menschen für Menschen.</p>
<p style="margin: 0; margin-top: 10px;">PS: Wenn Du keinen Account bei uns möchtest, kannst Du diese
E-Mail einfach ignorieren. ;)</p>
</td>
</tr>
<tr>
<td style="text-align: center; color :#17b53e">
<p></p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">Melde Dich gerne <a href="{{{ supportUrl }}}" style="color: #17b53e;">bei
unserem Support Team</a>, wenn Du Fragen hast.</p>
<p style="margin: 0; margin-top: 10px;">Bis bald bei <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a>!</p>
<p style="margin: 0; margin-bottom: 10px;"> Dein Human Connection Team</p>
</td>
</tr>
<tr>
<td style="display: none;">
<p></p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
</table>
<!-- Email Body German : END -->
<!-- Email Body English : BEGIN -->
<table class="email-english" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0"
width="100%" style="margin: auto;">
<tr>
<td style="padding: 20px 0; text-align: center">
</td>
</tr>
<!-- Hero Image, Flush : BEGIN -->
<tr>
<td style="background-color: #ffffff;">
<img
src="https://firebasestorage.googleapis.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LcGvGRsW6DrZn7FWRzF%2F-LcGv6EiVcsjYLfQ_2YE%2F-LcGv8UtmAWc61fxGveg%2Flets_get_together.png?generation=1555078880410873&alt=media"
width="600" height="" alt="Human Connection community logo" border="0"
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
class="g-img">
</td>
</tr>
<!-- Hero Image, Flush : END -->
<!-- 1 Column Text + Button : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Welcome to Human Connection!</h1>
<p style="margin: 0;">Thank you for joining our cause it's awesome to have you on board. There's
just one tiny step missing before we can start shaping the world together ... Please confirm your
e-mail address by clicking the button below:</p>
</td>
</tr>
<tr>
<td style="padding: 0 20px;">
<!-- Button : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0"
style="margin: auto;">
<tr>
<td class="button-td button-td-primary" style="border-radius: 4px; background: #17b53e;">
<a class="button-a button-a-primary" href="{{{ actionUrl }}}"
style="background: #17b53e; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;">Confirm
your e-mail address</a>
</td>
</tr>
</table>
<!-- Button : END -->
</td>
</tr>
<tr>
<td style="text-align: center; color :#17b53e">
<p></p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text + Button : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">If you didn't sign up for <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a> we recommend you to check it out!
It's a social network from people for people who want to connect and change the world together.</p>
<p style="margin: 0; margin-top: 10px;">PS: If you ignore this e-mail we will not create an account
for
you. ;)</p>
</td>
</tr>
<tr>
<td style="text-align: center; color :#17b53e">
<p></p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">Feel free to <a href="{{{ supportUrl }}}" style="color: #17b53e;">contact our
support team</a> with any
questions you have.</p>
<p style="margin: 0; margin-top: 10px;">See you soon on <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a>!</p>
<p style="margin: 0; margin-bottom: 10px;"> The Human Connection Team</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
</table>
<!-- Email Body English : END -->
<!-- Email Footer : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
style="margin: auto;">
<!-- 1 Column Text + Button : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; font-family: Lato, sans-serif; font-size: 12px; line-height: 15px; text-align: center; color: #888888;">
<br><br>
Human Connection gGmbH<br><span class="unstyle-auto-detected-links">Bahnhofstraße 11, 73235 Weilheim /
Teck<br>Germany</span>
<br><br>
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Willkommen bei Human Connection!</h1>
<p style="margin: 0;">Danke, dass Du dich angemeldet hast wir freuen uns, Dich dabei zu haben. Jetzt
fehlt nur noch eine Kleinigkeit, bevor wir gemeinsam die Welt verbessern können ... Bitte bestätige
Deine E-Mail Adresse:</p>
</td>
</tr>
<tr>
<td style="padding: 0 20px;">
<!-- Button : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin: auto;">
<tr>
<td class="button-td button-td-primary" style="border-radius: 4px; background: #17b53e;">
<a class="button-a button-a-primary" href="{{{ actionUrl }}}"
style="background: #17b53e; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;">Bestätige
Deine E-Mail Adresse</a>
</td>
</tr>
</table>
<!-- Button : END -->
</td>
</tr>
<tr>
<td style="text-align: center; color :#17b53e">
<p></p>
</td>
</tr>
</table>
<!-- Email Footer : END -->
</td>
</tr>
<!-- 1 Column Text + Button : END -->
<!--[if mso]>
</td>
</tr>
</table>
<![endif]-->
</div>
<!--[if mso | IE]>
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">Falls Du Dich nicht selbst bei <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a> angemeldet hast, schau doch mal vorbei!
Wir sind ein gemeinnütziges Aktionsnetzwerk von Menschen für Menschen.</p>
<p style="margin: 0; margin-top: 10px;">PS: Wenn Du keinen Account bei uns möchtest, kannst Du diese
E-Mail einfach ignorieren. ;)</p>
</td>
</tr>
<tr>
<td style="text-align: center; color :#17b53e">
<p></p>
</td>
</tr>
</table>
<![endif]-->
</center>
</body>
</td>
</tr>
<!-- 1 Column Text : END -->
</html>
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">Melde Dich gerne <a href="{{{ supportUrl }}}" style="color: #17b53e;">bei
unserem Support Team</a>, wenn Du Fragen hast.</p>
<p style="margin: 0; margin-top: 10px;">Bis bald bei <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a>!</p>
<p style="margin: 0; margin-bottom: 10px;"> Dein Human Connection Team</p>
</td>
</tr>
<tr>
<td style="display: none;">
<p></p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
</table>
<!-- Email Body German : END -->
<!-- Email Body English : BEGIN -->
<table class="email-english" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
style="margin: auto;">
<tr>
<td style="padding: 20px 0; text-align: center">
</td>
</tr>
<!-- Hero Image, Flush : BEGIN -->
<tr>
<td style="background-color: #ffffff;">
<img
src="https://firebasestorage.googleapis.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LcGvGRsW6DrZn7FWRzF%2F-LcGv6EiVcsjYLfQ_2YE%2F-LcGv8UtmAWc61fxGveg%2Flets_get_together.png?generation=1555078880410873&alt=media"
width="600" height="" alt="Human Connection community logo" border="0"
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
class="g-img">
</td>
</tr>
<!-- Hero Image, Flush : END -->
<!-- 1 Column Text + Button : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Welcome to Human Connection!</h1>
<p style="margin: 0;">Thank you for joining our cause it's awesome to have you on board. There's
just one tiny step missing before we can start shaping the world together ... Please confirm your
e-mail address by clicking the button below:</p>
</td>
</tr>
<tr>
<td style="padding: 0 20px;">
<!-- Button : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin: auto;">
<tr>
<td class="button-td button-td-primary" style="border-radius: 4px; background: #17b53e;">
<a class="button-a button-a-primary" href="{{{ actionUrl }}}"
style="background: #17b53e; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;">Confirm
your e-mail address</a>
</td>
</tr>
</table>
<!-- Button : END -->
</td>
</tr>
<tr>
<td style="text-align: center; color :#17b53e">
<p></p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text + Button : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">If you didn't sign up for <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a> we recommend you to check it out!
It's a social network from people for people who want to connect and change the world together.</p>
<p style="margin: 0; margin-top: 10px;">PS: If you ignore this e-mail we will not create an account
for
you. ;)</p>
</td>
</tr>
<tr>
<td style="text-align: center; color :#17b53e">
<p></p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">Feel free to <a href="{{{ supportUrl }}}" style="color: #17b53e;">contact our
support team</a> with any
questions you have.</p>
<p style="margin: 0; margin-top: 10px;">See you soon on <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a>!</p>
<p style="margin: 0; margin-bottom: 10px;"> The Human Connection Team</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
</table>
<!-- Email Body English : END -->

View File

@ -1,48 +0,0 @@
import fs from 'fs'
import path from 'path'
import mustache from 'mustache'
import CONFIG from '../../../config'
const from = '"Human Connection" <info@human-connection.org>'
const supportUrl = 'https://human-connection.org/en/contact'
const signupHtml = fs.readFileSync(path.join(__dirname, './signup.html'), 'utf-8')
const passwordResetHtml = fs.readFileSync(path.join(__dirname, './resetPassword.html'), 'utf-8')
const wrongAccountHtml = fs.readFileSync(path.join(__dirname, './wrongAccount.html'), 'utf-8')
export const signupTemplate = ({ email, nonce }) => {
const actionUrl = new URL('/registration/create-user-account', CONFIG.CLIENT_URI)
actionUrl.searchParams.set('nonce', nonce)
actionUrl.searchParams.set('email', email)
return {
from,
to: email,
subject: 'Willkommen, Bienvenue, Welcome to Human Connection!',
html: mustache.render(signupHtml, { actionUrl, supportUrl }),
}
}
export const resetPasswordTemplate = ({ email, nonce, name }) => {
const actionUrl = new URL('/password-reset/change-password', CONFIG.CLIENT_URI)
actionUrl.searchParams.set('nonce', nonce)
actionUrl.searchParams.set('email', email)
return {
from,
to: email,
subject: 'Neues Passwort | Reset Password',
html: mustache.render(passwordResetHtml, { actionUrl, name, nonce, supportUrl }),
}
}
export const wrongAccountTemplate = ({ email }) => {
const actionUrl = new URL('/password-reset/request', CONFIG.CLIENT_URI)
return {
from,
to: email,
subject: 'Falsche Mailadresse? | Wrong E-mail?',
html: mustache.render(wrongAccountHtml, { actionUrl, supportUrl }),
}
}

View File

@ -1,448 +1,189 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:o="urn:schemas-microsoft-com:office:office">
<!-- Email Body German : BEGIN -->
<table class="email-german" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
style="margin: auto;">
<tr>
<td style="padding: 20px 0; text-align: center">
</td>
</tr>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="x-apple-disable-message-reformatting">
<meta name="format-detection" content="telephone=no,address=no,email=no,date=no,url=no">
<title>Falsche Mailadresse? | Wrong E-mail?</title>
<!-- Hero Image, Flush : BEGIN -->
<tr>
<td style="background-color: #ffffff;">
<img
src="https://firebasestorage.googleapis.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LcGvGRsW6DrZn7FWRzF%2F-LcGv6EiVcsjYLfQ_2YE%2F-LcGv8UtmAWc61fxGveg%2Flets_get_together.png?generation=1555078880410873&alt=media"
width="600" height="" alt="Human Connection community logo" border="0"
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
class="g-img">
</td>
</tr>
<!-- Hero Image, Flush : END -->
<!--[if mso]>
<style>
* {
font-family: sans-serif !important;
}
</style>
<![endif]-->
<!--[if !mso]><!-->
<link href='https://fonts.googleapis.com/css?family=Lato:400,700' rel='stylesheet' type='text/css'>
<!--<![endif]-->
<!-- CSS RESETS -->
<style>
html,
body {
margin: 0 !important;
padding: 0 !important;
height: 100% !important;
width: 100% !important;
}
* {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
div[style*="margin: 16px 0"] {
margin: 0 !important;
}
table,
td {
mso-table-lspace: 0pt !important;
mso-table-rspace: 0pt !important;
}
table {
border-spacing: 0 !important;
border-collapse: collapse !important;
table-layout: fixed !important;
margin: 0 auto !important;
}
img {
-ms-interpolation-mode: bicubic;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a[x-apple-data-detectors],
.unstyle-auto-detected-links a,
.aBn {
border-bottom: 0 !important;
cursor: default !important;
color: inherit !important;
text-decoration: none !important;
font-size: inherit !important;
font-family: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
}
.a6S {
display: none !important;
opacity: 0.01 !important;
}
.im {
color: inherit !important;
}
img.g-img+div {
display: none !important;
}
/* iPhone 4, 4S, 5, 5S, 5C, and 5SE */
@media only screen and (min-device-width: 320px) and (max-device-width: 374px) {
u~div .email-container {
min-width: 320px !important;
}
}
/* iPhone 6, 6S, 7, 8, and X */
@media only screen and (min-device-width: 375px) and (max-device-width: 413px) {
u~div .email-container {
min-width: 375px !important;
}
}
/* iPhone 6+, 7+, and 8+ */
@media only screen and (min-device-width: 414px) {
u~div .email-container {
min-width: 414px !important;
}
}
</style>
<!--[if gte mso 9]>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
<![endif]-->
<!-- PROGRESSIVE ENHANCEMENTS -->
<style>
.button-td,
.button-a {
transition: all 100ms ease-in;
}
.button-td-primary:hover,
.button-a-primary:hover {
background: #19c243 !important;
border-color: #555555 !important;
}
@media screen and (max-width: 600px) {
.email-container p {
font-size: 17px !important;
}
}
</style>
<!-- LANGUAGE TOGGLE -->
<style>
.toggle+label {
display: inline-block;
height: 40px;
padding: 0 12px;
margin-top: 40px;
line-height: 38px;
font-family: Lato, sans-serif;
font-size: 16px;
border: 1px solid #cbc7d1;
cursor: pointer;
}
.toggle+label:hover {
background-color: #bee876;
}
.toggle:checked+label {
background-color: #19c243;
color: white;
}
.toggle-english+label {
border-bottom-right-radius: 50px;
border-top-right-radius: 50px;
border-left: none;
margin-left: -2px;
}
.toggle-german+label {
border-bottom-left-radius: 50px;
border-top-left-radius: 50px;
border-right: none;
margin-right: -2px;
}
.toggle-german:checked~table.email-german {
display: block;
}
.toggle-german:checked~table.email-english {
display: none;
}
.toggle-english:checked~table.email-english {
display: block;
}
.toggle-english:checked~table.email-german {
display: none;
}
</style>
</head>
<body width="100%" style="margin: 0; padding: 0 !important; mso-line-height-rule: exactly; background-color: #f5f4f6;">
<center style="width: 100%; background-color: #f5f4f6;">
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: #f5f4f6;">
<tr>
<td>
<![endif]-->
<div style="max-width: 600px; margin: 0 auto;" class="email-container">
<!--[if mso]>
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="600">
<tr>
<td>
<![endif]-->
<!-- LANGUAGE TOGGLE -->
<input type="radio" name="language" class="toggle toggle-german" style="display: none;" id="toggle-german"
checked="checked">
<label for="toggle-german">Deutsch</label>
<input type="radio" name="language" class="toggle toggle-english" style="display:none;" id="toggle-english">
<label for="toggle-english">English</label>
<p style="margin: 0;"></p>
<!-- Email Body German : BEGIN -->
<table class="email-german" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0"
width="100%" style="margin: auto;">
<tr>
<td style="padding: 20px 0; text-align: center">
</td>
</tr>
<!-- Hero Image, Flush : BEGIN -->
<tr>
<td style="background-color: #ffffff;">
<img
src="https://firebasestorage.googleapis.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LcGvGRsW6DrZn7FWRzF%2F-LcGv6EiVcsjYLfQ_2YE%2F-LcGv8UtmAWc61fxGveg%2Flets_get_together.png?generation=1555078880410873&alt=media"
width="600" height="" alt="Human Connection community logo" border="0"
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
class="g-img">
</td>
</tr>
<!-- Hero Image, Flush : END -->
<!-- 1 Column Text + Button : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Hallo!</h1>
<p style="margin: 0;">Du hast bei uns ein neues Password angefordert leider haben wir aber keinen
Account mit Deiner E-Mailadresse gefunden. Kann es sein, dass Du mit einer anderen Adresse bei uns
angemeldet bist?</p>
</td>
</tr>
<tr>
<td style="padding: 0 20px;">
<!-- Button : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0"
style="margin: auto;">
<tr>
<td class="button-td button-td-primary" style="border-radius: 4px; background: #17b53e;">
<a class="button-a button-a-primary" href="{{{ actionUrl }}}"
style="background: #17b53e; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;">Versuch'
es mit einer anderen E-Mail</a>
</td>
</tr>
</table>
<!-- Button : END -->
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text + Button : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">Wenn Du noch keinen Account bei <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a> hast oder Dein Password gar nicht ändern willst,
kannst Du diese E-Mail einfach ignorieren!</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">Ansonsten hilft Dir <a href="{{{ supportUrl }}}" style="color: #17b53e;">unser
Support Team</a> gerne weiter.</p>
<p style="margin: 0; margin-top: 10px;">Bis bald bei <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a>!</p>
<p style="margin: 0; margin-bottom: 10px;"> Dein Human Connection Team</p>
</td>
</tr>
<tr>
<td style="display: none;">
<p></p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
</table>
<!-- Email Body German : END -->
<!-- Email Body English : BEGIN -->
<table class="email-english" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0"
width="100%" style="margin: auto;">
<tr>
<td style="padding: 20px 0; text-align: center">
</td>
</tr>
<!-- Hero Image, Flush : BEGIN -->
<tr>
<td style="background-color: #ffffff;">
<img
src="https://firebasestorage.googleapis.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LcGvGRsW6DrZn7FWRzF%2F-LcGv6EiVcsjYLfQ_2YE%2F-LcGv8UtmAWc61fxGveg%2Flets_get_together.png?generation=1555078880410873&alt=media"
width="600" height="" alt="Human Connection community logo" border="0"
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
class="g-img">
</td>
</tr>
<!-- Hero Image, Flush : END -->
<!-- 1 Column Text + Button : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Hello!</h1>
<p style="margin: 0;">You requested a password reset but unfortunately we couldn't find an account
associated with your e-mail address. Did you maybe use another one when you signed up?</p>
</td>
</tr>
<tr>
<td style="padding: 0 20px;">
<!-- Button : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0"
style="margin: auto;">
<tr>
<td class="button-td button-td-primary" style="border-radius: 4px; background: #17b53e;">
<a class="button-a button-a-primary" href="{{{ actionUrl }}}"
style="background: #17b53e; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;">Try
a different e-mail</a>
</td>
</tr>
</table>
<!-- Button : END -->
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text + Button : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">If you don't have an account at <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a> yet or if you didn't want to reset your password,
please ignore this e-mail.</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">Otherwise <a href="{{{ supportUrl }}}" style="color: #17b53e;">our
support team</a> will be happy to help you out.</p>
<p style="margin: 0; margin-top: 10px;">See you soon on <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a>!</p>
<p style="margin: 0; margin-bottom: 10px;"> The Human Connection Team</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
</table>
<!-- Email Body English : END -->
<!-- Email Footer : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
style="margin: auto;">
<!-- 1 Column Text + Button : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; font-family: Lato, sans-serif; font-size: 12px; line-height: 15px; text-align: center; color: #888888;">
<br><br>
Human Connection gGmbH<br><span class="unstyle-auto-detected-links">Bahnhofstraße 11, 73235 Weilheim /
Teck<br>Germany</span>
<br><br>
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Hallo!</h1>
<p style="margin: 0;">Du hast bei uns ein neues Password angefordert leider haben wir aber keinen
Account mit Deiner E-Mailadresse gefunden. Kann es sein, dass Du mit einer anderen Adresse bei uns
angemeldet bist?</p>
</td>
</tr>
<tr>
<td style="padding: 0 20px;">
<!-- Button : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin: auto;">
<tr>
<td class="button-td button-td-primary" style="border-radius: 4px; background: #17b53e;">
<a class="button-a button-a-primary" href="{{{ actionUrl }}}"
style="background: #17b53e; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;">Versuch'
es mit einer anderen E-Mail</a>
</td>
</tr>
</table>
<!-- Button : END -->
</td>
</tr>
</table>
<!-- Email Footer : END -->
</td>
</tr>
<!-- 1 Column Text + Button : END -->
<!--[if mso]>
</td>
</tr>
</table>
<![endif]-->
</div>
<!--[if mso | IE]>
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">Wenn Du noch keinen Account bei <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a> hast oder Dein Password gar nicht ändern willst,
kannst Du diese E-Mail einfach ignorieren!</p>
</td>
</tr>
</table>
<![endif]-->
</center>
</body>
</td>
</tr>
<!-- 1 Column Text : END -->
</html>
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">Ansonsten hilft Dir <a href="{{{ supportUrl }}}" style="color: #17b53e;">unser
Support Team</a> gerne weiter.</p>
<p style="margin: 0; margin-top: 10px;">Bis bald bei <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a>!</p>
<p style="margin: 0; margin-bottom: 10px;"> Dein Human Connection Team</p>
</td>
</tr>
<tr>
<td style="display: none;">
<p></p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
</table>
<!-- Email Body German : END -->
<!-- Email Body English : BEGIN -->
<table class="email-english" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
style="margin: auto;">
<tr>
<td style="padding: 20px 0; text-align: center">
</td>
</tr>
<!-- Hero Image, Flush : BEGIN -->
<tr>
<td style="background-color: #ffffff;">
<img
src="https://firebasestorage.googleapis.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LcGvGRsW6DrZn7FWRzF%2F-LcGv6EiVcsjYLfQ_2YE%2F-LcGv8UtmAWc61fxGveg%2Flets_get_together.png?generation=1555078880410873&alt=media"
width="600" height="" alt="Human Connection community logo" border="0"
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
class="g-img">
</td>
</tr>
<!-- Hero Image, Flush : END -->
<!-- 1 Column Text + Button : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Hello!</h1>
<p style="margin: 0;">You requested a password reset but unfortunately we couldn't find an account
associated with your e-mail address. Did you maybe use another one when you signed up?</p>
</td>
</tr>
<tr>
<td style="padding: 0 20px;">
<!-- Button : BEGIN -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin: auto;">
<tr>
<td class="button-td button-td-primary" style="border-radius: 4px; background: #17b53e;">
<a class="button-a button-a-primary" href="{{{ actionUrl }}}"
style="background: #17b53e; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;">Try
a different e-mail</a>
</td>
</tr>
</table>
<!-- Button : END -->
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text + Button : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">If you don't have an account at <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a> yet or if you didn't want to reset your password,
please ignore this e-mail.</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
<!-- 1 Column Text : BEGIN -->
<tr>
<td style="background-color: #ffffff; padding: 0 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
<p style="margin: 0;">Otherwise <a href="{{{ supportUrl }}}" style="color: #17b53e;">our
support team</a> will be happy to help you out.</p>
<p style="margin: 0; margin-top: 10px;">See you soon on <a href="https://human-connection.org"
style="color: #17b53e;">Human Connection</a>!</p>
<p style="margin: 0; margin-bottom: 10px;"> The Human Connection Team</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
</table>
<!-- Email Body English : END -->

View File

@ -170,6 +170,8 @@ const permissions = shield(
block: isAuthenticated,
unblock: isAuthenticated,
markAsRead: isAuthenticated,
AddEmailAddress: isAuthenticated,
VerifyEmailAddress: isAuthenticated,
},
User: {
email: isMyOwn,

View File

@ -0,0 +1,12 @@
module.exports = {
email: { type: 'string', primary: true, lowercase: true, email: true },
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
nonce: { type: 'string', token: true },
belongsTo: {
type: 'relationship',
relationship: 'BELONGS_TO',
target: 'User',
direction: 'out',
eager: true,
},
}

View File

@ -5,6 +5,7 @@ export default {
User: require('./User.js'),
InvitationCode: require('./InvitationCode.js'),
EmailAddress: require('./EmailAddress.js'),
UnverifiedEmailAddress: require('./UnverifiedEmailAddress.js'),
SocialMedia: require('./SocialMedia.js'),
Post: require('./Post.js'),
Comment: require('./Comment.js'),

View File

@ -0,0 +1,92 @@
import generateNonce from './helpers/generateNonce'
import Resolver from './helpers/Resolver'
import existingEmailAddress from './helpers/existingEmailAddress'
import { UserInputError } from 'apollo-server'
import Validator from 'neode/build/Services/Validator.js'
export default {
Mutation: {
AddEmailAddress: async (_parent, args, context, _resolveInfo) => {
let response
try {
const { neode } = context
await new Validator(neode, neode.model('UnverifiedEmailAddress'), args)
} catch (e) {
throw new UserInputError('must be a valid email')
}
// check email does not belong to anybody
await existingEmailAddress(_parent, args, context)
const nonce = generateNonce()
const {
user: { id: userId },
} = context
const { email } = args
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async txc => {
const result = await txc.run(
`
MATCH (user:User {id: $userId})
MERGE (user)<-[:BELONGS_TO]-(email:UnverifiedEmailAddress {email: $email, nonce: $nonce})
SET email.createdAt = toString(datetime())
RETURN email, user
`,
{ userId, email, nonce },
)
return result.records.map(record => ({
name: record.get('user').properties.name,
...record.get('email').properties,
}))
})
try {
const txResult = await writeTxResultPromise
response = txResult[0]
} finally {
session.close()
}
return response
},
VerifyEmailAddress: async (_parent, args, context, _resolveInfo) => {
let response
const {
user: { id: userId },
} = context
const { nonce, email } = args
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async txc => {
const result = await txc.run(
`
MATCH (user:User {id: $userId})-[:PRIMARY_EMAIL]->(previous:EmailAddress)
MATCH (user)<-[:BELONGS_TO]-(email:UnverifiedEmailAddress {email: $email, nonce: $nonce})
MERGE (user)-[:PRIMARY_EMAIL]->(email)
SET email:EmailAddress
SET email.verifiedAt = toString(datetime())
REMOVE email:UnverifiedEmailAddress
DETACH DELETE previous
RETURN email
`,
{ userId, email, nonce },
)
return result.records.map(record => record.get('email').properties)
})
try {
const txResult = await writeTxResultPromise
response = txResult[0]
} catch (e) {
if (e.code === 'Neo.ClientError.Schema.ConstraintValidationFailed')
throw new UserInputError('A user account with this email already exists.')
throw new Error(e)
} finally {
session.close()
}
if (!response) throw new UserInputError('Invalid nonce or no email address found.')
return response
},
},
EmailAddress: {
...Resolver('EmailAddress', {
undefinedToNull: ['verifiedAt'],
}),
},
}

View File

@ -0,0 +1,298 @@
import Factory from '../../seed/factories'
import { gql } from '../../jest/helpers'
import { getDriver, neode as getNeode } from '../../bootstrap/neo4j'
import createServer from '../../server'
import { createTestClient } from 'apollo-server-testing'
const factory = Factory()
const neode = getNeode()
let mutate
let authenticatedUser
let user
let variables
const driver = getDriver()
beforeEach(async () => {
variables = {}
})
beforeAll(() => {
const { server } = createServer({
context: () => {
return {
driver,
neode,
user: authenticatedUser,
}
},
})
mutate = createTestClient(server).mutate
})
afterEach(async () => {
await factory.cleanDatabase()
})
describe('AddEmailAddress', () => {
const mutation = gql`
mutation($email: String!) {
AddEmailAddress(email: $email) {
email
verifiedAt
createdAt
}
}
`
beforeEach(() => {
variables = { ...variables, email: 'new-email@example.org' }
})
describe('unauthenticated', () => {
beforeEach(() => {
authenticatedUser = null
})
it('throws AuthorizationError', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
data: { AddEmailAddress: null },
errors: [{ message: 'Not Authorised!' }],
})
})
})
describe('authenticated', () => {
beforeEach(async () => {
user = await factory.create('User', { id: '567', email: 'user@example.org' })
authenticatedUser = await user.toJson()
})
describe('email attribute is not a valid email', () => {
beforeEach(() => {
variables = { ...variables, email: 'foobar' }
})
it('throws UserInputError', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
data: { AddEmailAddress: null },
errors: [{ message: 'must be a valid email' }],
})
})
})
describe('email attribute is a valid email', () => {
it('creates a new unverified `EmailAddress` node', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
data: {
AddEmailAddress: {
email: 'new-email@example.org',
verifiedAt: null,
createdAt: expect.any(String),
},
},
errors: undefined,
})
})
it('connects `UnverifiedEmailAddress` to the authenticated user', async () => {
await mutate({ mutation, variables })
const result = await neode.cypher(`
MATCH(u:User)-[:PRIMARY_EMAIL]->(:EmailAddress {email: "user@example.org"})
MATCH(u:User)<-[:BELONGS_TO]-(e:UnverifiedEmailAddress {email: "new-email@example.org"})
RETURN e
`)
const email = neode.hydrateFirst(result, 'e', neode.model('UnverifiedEmailAddress'))
await expect(email.toJson()).resolves.toMatchObject({
email: 'new-email@example.org',
nonce: expect.any(String),
})
})
describe('if another `UnverifiedEmailAddress` node already exists with that email', () => {
it('throws no unique constraint violation error', async () => {
await factory.create('UnverifiedEmailAddress', {
createdAt: '2019-09-24T14:00:01.565Z',
email: 'new-email@example.org',
})
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
data: {
AddEmailAddress: {
email: 'new-email@example.org',
verifiedAt: null,
},
},
errors: undefined,
})
})
})
describe('but if another user owns an `EmailAddress` already with that email', () => {
it('throws UserInputError because of unique constraints', async () => {
await factory.create('User', { email: 'new-email@example.org' })
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
data: { AddEmailAddress: null },
errors: [{ message: 'A user account with this email already exists.' }],
})
})
})
})
})
})
describe('VerifyEmailAddress', () => {
const mutation = gql`
mutation($email: String!, $nonce: String!) {
VerifyEmailAddress(email: $email, nonce: $nonce) {
email
createdAt
verifiedAt
}
}
`
beforeEach(() => {
variables = { ...variables, email: 'to-be-verified@example.org', nonce: '123456' }
})
describe('unauthenticated', () => {
beforeEach(() => {
authenticatedUser = null
})
it('throws AuthorizationError', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
data: { VerifyEmailAddress: null },
errors: [{ message: 'Not Authorised!' }],
})
})
})
describe('authenticated', () => {
beforeEach(async () => {
user = await factory.create('User', { id: '567', email: 'user@example.org' })
authenticatedUser = await user.toJson()
})
describe('if no unverified `EmailAddress` node exists', () => {
it('throws UserInputError', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
data: { VerifyEmailAddress: null },
errors: [{ message: 'Invalid nonce or no email address found.' }],
})
})
})
describe('given a `UnverifiedEmailAddress`', () => {
let emailAddress
beforeEach(async () => {
emailAddress = await factory.create('UnverifiedEmailAddress', {
nonce: 'abcdef',
verifiedAt: null,
createdAt: new Date().toISOString(),
email: 'to-be-verified@example.org',
})
})
describe('given invalid nonce', () => {
it('throws UserInputError', async () => {
variables.nonce = 'asdfgh'
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
data: { VerifyEmailAddress: null },
errors: [{ message: 'Invalid nonce or no email address found.' }],
})
})
})
describe('given valid nonce for `UnverifiedEmailAddress` node', () => {
beforeEach(() => {
variables = { ...variables, nonce: 'abcdef' }
})
describe('but the address does not belong to the authenticated user', () => {
it('throws UserInputError', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
data: { VerifyEmailAddress: null },
errors: [{ message: 'Invalid nonce or no email address found.' }],
})
})
})
describe('and the `UnverifiedEmailAddress` belongs to the authenticated user', () => {
beforeEach(async () => {
await emailAddress.relateTo(user, 'belongsTo')
})
it('adds `verifiedAt`', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
data: {
VerifyEmailAddress: {
email: 'to-be-verified@example.org',
verifiedAt: expect.any(String),
createdAt: expect.any(String),
},
},
errors: undefined,
})
})
it('connects the new `EmailAddress` as PRIMARY', async () => {
await mutate({ mutation, variables })
const result = await neode.cypher(`
MATCH(u:User {id: "567"})-[:PRIMARY_EMAIL]->(e:EmailAddress {email: "to-be-verified@example.org"})
RETURN e
`)
const email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress'))
await expect(email.toJson()).resolves.toMatchObject({
email: 'to-be-verified@example.org',
})
})
it('removes previous PRIMARY relationship', async () => {
const cypherStatement = `
MATCH(u:User {id: "567"})-[:PRIMARY_EMAIL]->(e:EmailAddress {email: "user@example.org"})
RETURN e
`
let result = await neode.cypher(cypherStatement)
let email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress'))
await expect(email.toJson()).resolves.toMatchObject({
email: 'user@example.org',
})
await mutate({ mutation, variables })
result = await neode.cypher(cypherStatement)
email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress'))
await expect(email).toBe(false)
})
it('removes previous `EmailAddress` node', async () => {
const cypherStatement = `
MATCH(u:User {id: "567"})<-[:BELONGS_TO]-(e:EmailAddress {email: "user@example.org"})
RETURN e
`
let result = await neode.cypher(cypherStatement)
let email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress'))
await expect(email.toJson()).resolves.toMatchObject({
email: 'user@example.org',
})
await mutate({ mutation, variables })
result = await neode.cypher(cypherStatement)
email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress'))
await expect(email).toBe(false)
})
describe('Edge case: In the meantime someone created an `EmailAddress` node with the given email', () => {
beforeEach(async () => {
await factory.create('EmailAddress', { email: 'to-be-verified@example.org' })
})
it('throws UserInputError because of unique constraints', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
data: { VerifyEmailAddress: null },
errors: [{ message: 'A user account with this email already exists.' }],
})
})
})
})
})
})
})
})

View File

@ -0,0 +1,26 @@
import { UserInputError } from 'apollo-server'
export default async function alreadyExistingMail(_parent, args, context) {
let { email } = args
email = email.toLowerCase()
const cypher = `
MATCH (email:EmailAddress {email: $email})
OPTIONAL MATCH (email)-[:BELONGS_TO]-(user)
RETURN email, user
`
let transactionRes
const session = context.driver.session()
try {
transactionRes = await session.run(cypher, { email })
} finally {
session.close()
}
const [result] = transactionRes.records.map(record => {
return {
alreadyExistingEmail: record.get('email').properties,
user: record.get('user') && record.get('user').properties,
}
})
const { alreadyExistingEmail, user } = result || {}
if (user) throw new UserInputError('A user account with this email already exists.')
return alreadyExistingEmail
}

View File

@ -0,0 +1,4 @@
import uuid from 'uuid/v4'
export default function generateNonce() {
return uuid().substring(0, 6)
}

View File

@ -1,41 +1,16 @@
import { UserInputError } from 'apollo-server'
import uuid from 'uuid/v4'
import { neode } from '../../bootstrap/neo4j'
import fileUpload from './fileUpload'
import encryptPassword from '../../helpers/encryptPassword'
import generateNonce from './helpers/generateNonce'
import existingEmailAddress from './helpers/existingEmailAddress'
const instance = neode()
const alreadyExistingMail = async (_parent, args, context) => {
let { email } = args
email = email.toLowerCase()
const cypher = `
MATCH (email:EmailAddress {email: $email})
OPTIONAL MATCH (email)-[:PRIMARY_EMAIL]-(user)
RETURN email, user
`
let transactionRes
const session = context.driver.session()
try {
transactionRes = await session.run(cypher, { email })
} finally {
session.close()
}
const [result] = transactionRes.records.map(record => {
return {
alreadyExistingEmail: record.get('email').properties,
user: record.get('user') && record.get('user').properties,
}
})
const { alreadyExistingEmail, user } = result || {}
if (user) throw new UserInputError('User account with this email already exists.')
return alreadyExistingEmail
}
export default {
Mutation: {
CreateInvitationCode: async (_parent, args, context, _resolveInfo) => {
args.token = uuid().substring(0, 6)
args.token = generateNonce()
const {
user: { id: userId },
} = context
@ -54,9 +29,9 @@ export default {
return response
},
Signup: async (_parent, args, context) => {
const nonce = uuid().substring(0, 6)
const nonce = generateNonce()
args.nonce = nonce
let emailAddress = await alreadyExistingMail(_parent, args, context)
let emailAddress = await existingEmailAddress(_parent, args, context)
if (emailAddress) return emailAddress
try {
emailAddress = await instance.create('EmailAddress', args)
@ -67,9 +42,9 @@ export default {
},
SignupByInvitation: async (_parent, args, context) => {
const { token } = args
const nonce = uuid().substring(0, 6)
const nonce = generateNonce()
args.nonce = nonce
let emailAddress = await alreadyExistingMail(_parent, args, context)
let emailAddress = await existingEmailAddress(_parent, args, context)
if (emailAddress) return emailAddress
try {
const result = await instance.cypher(

View File

@ -257,7 +257,7 @@ describe('SignupByInvitation', () => {
it('throws unique violation error', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
errors: [{ message: 'User account with this email already exists.' }],
errors: [{ message: 'A user account with this email already exists.' }],
})
})
})
@ -307,6 +307,7 @@ describe('Signup', () => {
it('is allowed to signup users by email', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
data: { Signup: { email: 'someuser@example.org' } },
errors: undefined,
})
})
@ -342,7 +343,7 @@ describe('Signup', () => {
it('throws UserInputError error because of unique constraint violation', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
errors: [{ message: 'User account with this email already exists.' }],
errors: [{ message: 'A user account with this email already exists.' }],
})
})
})
@ -351,6 +352,7 @@ describe('Signup', () => {
it('resolves with the already existing email', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
data: { Signup: { email: 'someuser@example.org' } },
errors: undefined,
})
})
@ -359,6 +361,7 @@ describe('Signup', () => {
await expect(neode.all('EmailAddress')).resolves.toHaveLength(2)
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
data: { Signup: { email: 'someuser@example.org' } },
errors: undefined,
})
await expect(neode.all('EmailAddress')).resolves.toHaveLength(2)
})

View File

@ -20,4 +20,9 @@ type Mutation {
about: String
termsAndConditionsAgreedVersion: String!
): User
AddEmailAddress(email: String!): EmailAddress
VerifyEmailAddress(
nonce: String!
email: String!
): EmailAddress
}

View File

@ -1,16 +1,21 @@
import faker from 'faker'
export function defaults({ args }) {
const defaults = {
email: faker.internet.email(),
verifiedAt: new Date().toISOString(),
}
args = {
...defaults,
...args,
}
return args
}
export default function create() {
return {
factory: async ({ args, neodeInstance }) => {
const defaults = {
email: faker.internet.email(),
verifiedAt: new Date().toISOString(),
}
args = {
...defaults,
...args,
}
args = defaults({ args })
return neodeInstance.create('EmailAddress', args)
},
}

View File

@ -9,6 +9,7 @@ import createTag from './tags.js'
import createSocialMedia from './socialMedia.js'
import createLocation from './locations.js'
import createEmailAddress from './emailAddresses.js'
import createUnverifiedEmailAddresss from './unverifiedEmailAddresses.js'
export const seedServerHost = 'http://127.0.0.1:4001'
@ -32,6 +33,7 @@ const factories = {
SocialMedia: createSocialMedia,
Location: createLocation,
EmailAddress: createEmailAddress,
UnverifiedEmailAddress: createUnverifiedEmailAddresss,
}
export const cleanDatabase = async (options = {}) => {

View File

@ -0,0 +1,10 @@
import { defaults } from './emailAddresses.js'
export default function create() {
return {
factory: async ({ args, neodeInstance }) => {
args = defaults({ args })
return neodeInstance.create('UnverifiedEmailAddress', args)
},
}
}

View File

@ -115,7 +115,7 @@ describe('Signup', () => {
mocks.$apollo.mutate = jest
.fn()
.mockRejectedValue(
new Error('UserInputError: User account with this email already exists.'),
new Error('UserInputError: A user account with this email already exists.'),
)
})

View File

@ -128,7 +128,7 @@ export default {
} catch (err) {
const { message } = err
const mapping = {
'User account with this email already exists': 'email-exists',
'A user account with this email already exists': 'email-exists',
'Invitation code already used or does not exist': 'invalid-invitation-token',
}
for (const [pattern, key] of Object.entries(mapping)) {

View File

@ -0,0 +1,20 @@
import gql from 'graphql-tag'
export const AddEmailAddressMutation = gql`
mutation($email: String!) {
AddEmailAddress(email: $email) {
email
createdAt
}
}
`
export const VerifyEmailAddressMutation = gql`
mutation($email: String!, $nonce: String!) {
VerifyEmailAddress(email: $email, nonce: $nonce) {
email
verifiedAt
createdAt
}
}
`

View File

@ -158,6 +158,27 @@
"labelBio": "Über dich",
"success": "Deine Daten wurden erfolgreich aktualisiert!"
},
"email": {
"validation": {
"same-email": "Das ist deine aktuelle E-Mail Addresse"
},
"name": "Deine E-Mail",
"labelEmail": "E-Mail Adresse ändern",
"labelNewEmail": "Neue E-Mail Adresse",
"labelNonce": "Bestätigungscode eingeben",
"success": "Eine neue E-Mail Addresse wurde registriert.",
"submitted": "Eine E-Mail zur Bestätigung deiner Adresse wurde an <b>{email}</b> gesendet.",
"change-successful": "Deine E-Mail Adresse wurde erfolgreich geändert.",
"verification-error": {
"message": "Deine E-Mail Adresse konnte nicht verifiziert werden.",
"support": "Wenn das Problem weiterhin besteht, kontaktiere uns gerne per E-Mail an",
"explanation": "Das kann verschiedene Ursachen haben:",
"reason": {
"invalid-nonce": "Ist der Bestätigungscode falsch?",
"no-email-request": "Bist du dir sicher, dass du eine Änderung deiner E-Mail Adresse angefragt hattest?"
}
}
},
"validation": {
"slug": {
"regex": "Es sind nur Kleinbuchstaben, Zahlen, Unterstriche oder Bindestriche erlaubt.",

View File

@ -2,7 +2,7 @@
"maintenance": {
"title": "Human Connection is under maintenance",
"explanation": "At the moment we are doing some scheduled maintenance, please try again later.",
"questions": "Any Questions or concerns, send an email to"
"questions": "Any Questions or concerns, send an email to"
},
"index": {
"no-results": "No contributions found.",
@ -159,6 +159,27 @@
"labelBio": "About You",
"success": "Your data was successfully updated!"
},
"email": {
"validation": {
"same-email": "This is your current email address"
},
"name": "Your email",
"labelEmail": "Change your email address",
"labelNewEmail": "New email Address",
"labelNonce": "Enter your code",
"success": "A new email address has been registered.",
"submitted": "An email to verify your address has been sent to <b>{email}</b>.",
"change-successful": "Your email address has been changed successfully.",
"verification-error": {
"message": "Your email could not be changed.",
"explanation": "This can have different causes:",
"reason": {
"invalid-nonce": "Is the confirmation code invalid?",
"no-email-request": "Are you certain that you requested a change of your email address?"
},
"support": "If the problem persists, please contact us by email at"
}
},
"validation": {
"slug": {
"regex": "Allowed characters are only lowercase letters, numbers, underscores and hyphens.",
@ -254,7 +275,7 @@
"users": {
"name": "Users",
"form": {
"placeholder": "E-Mail, name or description"
"placeholder": "email, name or description"
},
"table": {
"columns": {

View File

@ -23,6 +23,10 @@ export default {
name: this.$t('settings.data.name'),
path: `/settings`,
},
{
name: this.$t('settings.email.name'),
path: `/settings/my-email-address`,
},
{
name: this.$t('settings.security.name'),
path: `/settings/security`,

View File

@ -31,14 +31,7 @@
:placeholder="$t('settings.data.labelBio')"
/>
<template slot="footer">
<ds-button
style="float: right;"
icon="check"
:disabled="errors"
type="submit"
:loading="loadingData"
primary
>
<ds-button icon="check" :disabled="errors" type="submit" :loading="loadingData" primary>
{{ $t('actions.save') }}
</ds-button>
</template>

View File

@ -0,0 +1,53 @@
import { mount, createLocalVue } from '@vue/test-utils'
import EnterNoncePage from './enter-nonce.vue'
import Styleguide from '@human-connection/styleguide'
const localVue = createLocalVue()
localVue.use(Styleguide)
describe('EnterNoncePage', () => {
let mocks
let wrapper
beforeEach(() => {
wrapper = null
mocks = {
$t: jest.fn(t => t),
$route: {
query: {},
},
$router: {
replace: jest.fn(),
},
}
})
describe('mount', () => {
const Wrapper = () => {
return mount(EnterNoncePage, {
mocks,
localVue,
})
}
describe('form', () => {
describe('submit', () => {
it('renders form errors', () => {
wrapper = Wrapper()
wrapper.find('form').trigger('submit')
expect(mocks.$router.replace).not.toHaveBeenCalled()
})
describe('entering a nonce', () => {
it('redirects to my-email-address/verify', () => {
wrapper = Wrapper()
wrapper.find('#nonce').setValue('foobar')
wrapper.find('form').trigger('submit')
expect(mocks.$router.replace).toHaveBeenCalled()
})
})
})
})
})
})

View File

@ -0,0 +1,59 @@
<template>
<ds-form v-model="form" :schema="formSchema" @submit="submit">
<template slot-scope="{ errors }">
<ds-card :header="$t('settings.email.name')">
<ds-input
id="email"
model="email"
icon="envelope"
disabled
:label="$t('settings.email.labelNewEmail')"
/>
<ds-input
id="nonce"
model="nonce"
icon="question-circle"
:label="$t('settings.email.labelNonce')"
/>
<template slot="footer">
<ds-button class="submit-button" icon="check" :disabled="errors" type="submit" primary>
{{ $t('actions.save') }}
</ds-button>
</template>
</ds-card>
</template>
</ds-form>
</template>
<script>
export default {
data() {
return {
formSchema: {
nonce: { type: 'string', required: true },
},
}
},
computed: {
form: {
get: function() {
const { email = '', nonce = '' } = this.$route.query
return { email, nonce }
},
set: function(formData) {
this.formData = formData
},
},
},
methods: {
async submit() {
const { email, nonce } = this.formData
this.$router.replace({
path: 'verify',
query: { email, nonce },
})
},
},
}
</script>

View File

@ -0,0 +1,116 @@
import { config, mount, createLocalVue } from '@vue/test-utils'
import EmailSettingsIndexPage from './index.vue'
import Vuex from 'vuex'
import Styleguide from '@human-connection/styleguide'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(Styleguide)
config.stubs['sweetalert-icon'] = '<span><slot /></span>'
describe('EmailSettingsIndexPage', () => {
let store
let mocks
let wrapper
beforeEach(() => {
wrapper = null
store = new Vuex.Store({
getters: {
'auth/user': () => {
return { id: 'u23', email: 'some-mail@example.org' }
},
},
})
mocks = {
$t: jest.fn(t => t),
$toast: {
success: jest.fn(),
error: jest.fn(),
},
$apollo: {
mutate: jest.fn().mockResolvedValue(),
},
$router: {
push: jest.fn(),
},
}
})
describe('mount', () => {
const Wrapper = () => {
return mount(EmailSettingsIndexPage, {
store,
mocks,
localVue,
})
}
describe('form', () => {
describe('submit', () => {
beforeEach(jest.useFakeTimers)
describe('email unchanged', () => {
beforeEach(() => {
wrapper = Wrapper()
wrapper.find('form').trigger('submit')
})
it('displays form errors', () => {
expect(wrapper.text()).not.toContain('settings.email.submitted')
expect(wrapper.text()).toContain('settings.email.validation.same-email')
})
it('does not call $apollo.mutate', () => {
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
})
describe('enter another email', () => {
beforeEach(() => {
wrapper = Wrapper()
wrapper.find('#email').setValue('yet-another-email@example.org')
wrapper.find('form').trigger('submit')
})
it('calls $apollo.mutate', () => {
expect(mocks.$apollo.mutate).toHaveBeenCalled()
})
it('no form errors', () => {
expect(wrapper.text()).not.toContain('settings.email.validation.same-email')
expect(wrapper.text()).toContain('settings.email.submitted')
})
describe('after timeout', () => {
beforeEach(jest.runAllTimers)
it('redirects to `my-email-address/enter-nonce`', () => {
expect(mocks.$router.push).toHaveBeenCalledWith({
path: 'my-email-address/enter-nonce',
query: { email: 'yet-another-email@example.org' },
})
})
})
})
describe('if backend responds with unique constraint violation', () => {
beforeEach(() => {
mocks.$apollo.mutate = jest.fn().mockRejectedValue({
message: 'User account already exists',
})
wrapper = Wrapper()
wrapper.find('#email').setValue('already-taken@example.org')
wrapper.find('form').trigger('submit')
})
it('translates error message', () => {
expect(wrapper.text()).toContain('registration.signup.form.errors.email-exists')
})
})
})
})
})
})

View File

@ -0,0 +1,113 @@
<template>
<ds-card centered v-if="success">
<transition name="ds-transition-fade">
<sweetalert-icon icon="info" />
</transition>
<ds-text v-html="submitMessage" />
</ds-card>
<ds-form v-else v-model="form" :schema="formSchema" @submit="submit">
<template slot-scope="{ errors }">
<ds-card :header="$t('settings.email.name')">
<ds-input
id="email"
model="email"
icon="envelope"
:label="$t('settings.email.labelEmail')"
/>
<template slot="footer">
<ds-space class="backendErrors" v-if="backendErrors">
<ds-text align="center" bold color="danger">{{ backendErrors.message }}</ds-text>
</ds-space>
<ds-button icon="check" :disabled="errors" type="submit" primary>
{{ $t('actions.save') }}
</ds-button>
</template>
</ds-card>
</template>
</ds-form>
</template>
<script>
import { mapGetters } from 'vuex'
import { AddEmailAddressMutation } from '~/graphql/EmailAddress.js'
import { SweetalertIcon } from 'vue-sweetalert-icons'
export default {
components: {
SweetalertIcon,
},
data() {
return {
backendErrors: null,
success: false,
}
},
computed: {
submitMessage() {
const { email } = this.formData
return this.$t('settings.email.submitted', { email })
},
...mapGetters({
currentUser: 'auth/user',
}),
form: {
get: function() {
const { email } = this.currentUser
return { email }
},
set: function(formData) {
this.formData = formData
},
},
formSchema() {
const { email } = this.currentUser
const sameEmailValidationError = this.$t('settings.email.validation.same-email')
return {
email: [
{ type: 'email', required: true },
{
validator(rule, value, callback, source, options) {
const errors = []
if (email === value) {
errors.push(sameEmailValidationError)
}
return errors
},
},
],
}
},
},
methods: {
async submit() {
const { email } = this.formData
try {
await this.$apollo.mutate({
mutation: AddEmailAddressMutation,
variables: { email },
})
this.$toast.success(this.$t('settings.email.success'))
this.success = true
setTimeout(() => {
this.$router.push({
path: 'my-email-address/enter-nonce',
query: { email },
})
}, 3000)
} catch (err) {
if (err.message.includes('exists')) {
// We cannot use form validation errors here, the backend does not
// have a query to filter for email addresses. This is a privacy
// consideration. We could implement a dedicated query to check that
// but I think it's too much effort for this feature.
this.backendErrors = { message: this.$t('registration.signup.form.errors.email-exists') }
return
}
this.$toast.error(err.message)
}
},
},
}
</script>

View File

@ -0,0 +1,164 @@
import { config, mount, createLocalVue } from '@vue/test-utils'
import EmailVerifyPage from './verify.vue'
import Vuex from 'vuex'
import Styleguide from '@human-connection/styleguide'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(Styleguide)
config.stubs['client-only'] = '<span><slot /></span>'
config.stubs['sweetalert-icon'] = '<span><slot /></span>'
describe('EmailVerifyPage', () => {
let store
let mocks
let wrapper
let setUser
beforeEach(() => {
setUser = jest.fn()
wrapper = null
store = new Vuex.Store({
getters: {
'auth/user': () => {
return { id: 'u23', email: 'some-mail@example.org' }
},
},
mutations: {
'auth/SET_USER': setUser,
},
})
mocks = {
$t: jest.fn(t => t),
$toast: {
success: jest.fn(),
error: jest.fn(),
},
$router: {
replace: jest.fn(),
},
store,
}
})
describe('asyncData', () => {
const asyncDataAction = () => {
const context = {
store: mocks.store,
query: {},
app: {
apolloProvider: {
defaultClient: mocks.$apollo,
},
},
}
return EmailVerifyPage.asyncData(context)
}
describe('backend sends successful response', () => {
beforeEach(() => {
mocks = {
...mocks,
$apollo: {
mutate: jest.fn().mockResolvedValue({
data: {
VerifyEmailAddress: {
email: 'verified-email@example.org',
},
},
}),
},
}
})
it('sets `success` to true', async () => {
await expect(asyncDataAction()).resolves.toEqual({
success: true,
})
})
it("updates current user's email", async () => {
await asyncDataAction()
expect(setUser).toHaveBeenCalledWith({}, { id: 'u23', email: 'verified-email@example.org' })
})
})
describe('backend sends unsuccessful response', () => {
beforeEach(() => {
mocks = {
...mocks,
$apollo: {
mutate: jest.fn().mockRejectedValue({
data: { VerifyEmailAddress: null },
errors: [{ message: 'User account already exists with that email' }],
}),
},
}
})
it('sets `success` to false', async () => {
await expect(asyncDataAction()).resolves.toEqual({
success: false,
})
})
it('does not updates current user', async () => {
await asyncDataAction()
expect(setUser).not.toHaveBeenCalled()
})
})
})
describe('mount', () => {
beforeEach(jest.useFakeTimers)
const Wrapper = () => {
return mount(EmailVerifyPage, {
store,
mocks,
localVue,
})
}
describe('given successful verification', () => {
beforeEach(() => {
mocks = { ...mocks, success: true }
wrapper = Wrapper()
})
it('shows success message', () => {
expect(wrapper.text()).toContain('settings.email.change-successful')
})
describe('after timeout', () => {
beforeEach(jest.runAllTimers)
it('redirects to email settings page', () => {
expect(mocks.$router.replace).toHaveBeenCalledWith({
name: 'settings-my-email-address',
})
})
})
})
describe('given unsuccessful verification', () => {
beforeEach(() => {
mocks = { ...mocks, success: false }
wrapper = Wrapper()
})
it('shows success message', () => {
expect(wrapper.text()).toContain('settings.email.verification-error')
})
describe('after timeout', () => {
beforeEach(jest.runAllTimers)
it('does not redirect', () => {
expect(mocks.$router.replace).not.toHaveBeenCalledWith()
})
})
})
})
})

View File

@ -0,0 +1,99 @@
<template>
<ds-card>
<transition name="ds-transition-fade">
<client-only>
<sweetalert-icon :icon="sweetAlertIcon" />
</client-only>
</transition>
<ds-space v-if="success">
<ds-text bold align="center">
{{ $t(`settings.email.change-successful`) }}
</ds-text>
</ds-space>
<template v-else>
<ds-text bold align="center">
{{ $t(`settings.email.verification-error.message`) }}
</ds-text>
<ds-space class="message">
<client-only>
<ds-text>
<ds-space margin-top="large" margin-bottom="small">
{{ $t(`settings.email.verification-error.explanation`) }}
</ds-space>
<ds-list>
<ds-list-item>
{{ $t(`settings.email.verification-error.reason.invalid-nonce`) }}
</ds-list-item>
<ds-list-item>
{{ $t(`settings.email.verification-error.reason.no-email-request`) }}
</ds-list-item>
</ds-list>
{{ $t('settings.email.verification-error.support') }}
<a href="mailto:support@human-connection.org">support@human-connection.org</a>
</ds-text>
</client-only>
</ds-space>
</template>
</ds-card>
</template>
<script>
import { VerifyEmailAddressMutation } from '~/graphql/EmailAddress.js'
import { SweetalertIcon } from 'vue-sweetalert-icons'
export default {
components: {
SweetalertIcon,
},
computed: {
sweetAlertIcon() {
return this.success ? 'success' : 'error'
},
},
created() {
if (this.success) {
setTimeout(() => {
this.$router.replace({ name: 'settings-my-email-address' })
}, 3000)
}
},
async asyncData(context) {
const {
store,
query,
app: { apolloProvider },
} = context
const client = apolloProvider.defaultClient
let success
const { email = '', nonce = '' } = query
const currentUser = store.getters['auth/user']
try {
const response = await client.mutate({
mutation: VerifyEmailAddressMutation,
variables: { email, nonce },
})
const {
data: { VerifyEmailAddress },
} = response
success = true
store.commit(
'auth/SET_USER',
{ ...currentUser, email: VerifyEmailAddress.email },
{ root: true },
)
} catch (error) {
success = false
}
return { success }
},
}
</script>
<style lang="scss" scoped>
.message {
display: flex;
justify-content: space-around;
}
</style>