mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Merge pull request #1711 from Human-Connection/407-change_your_email_address
407 change your email address
This commit is contained in:
commit
cfdf9dad2c
@ -5,7 +5,8 @@ import {
|
|||||||
signupTemplate,
|
signupTemplate,
|
||||||
resetPasswordTemplate,
|
resetPasswordTemplate,
|
||||||
wrongAccountTemplate,
|
wrongAccountTemplate,
|
||||||
} from './templates/templateBuilder'
|
emailVerificationTemplate,
|
||||||
|
} from './templateBuilder'
|
||||||
|
|
||||||
const hasEmailConfig = CONFIG.SMTP_HOST && CONFIG.SMTP_PORT
|
const hasEmailConfig = CONFIG.SMTP_HOST && CONFIG.SMTP_PORT
|
||||||
const hasAuthData = CONFIG.SMTP_USERNAME && CONFIG.SMTP_PASSWORD
|
const hasAuthData = CONFIG.SMTP_USERNAME && CONFIG.SMTP_PASSWORD
|
||||||
@ -57,8 +58,17 @@ const sendPasswordResetMail = async (resolve, root, args, context, resolveInfo)
|
|||||||
return true
|
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 {
|
export default {
|
||||||
Mutation: {
|
Mutation: {
|
||||||
|
AddEmailAddress: sendEmailVerificationMail,
|
||||||
requestPasswordReset: sendPasswordResetMail,
|
requestPasswordReset: sendPasswordResetMail,
|
||||||
Signup: sendSignupMail,
|
Signup: sendSignupMail,
|
||||||
SignupByInvitation: sendSignupMail,
|
SignupByInvitation: sendSignupMail,
|
||||||
|
|||||||
77
backend/src/middleware/email/templateBuilder.js
Normal file
77
backend/src/middleware/email/templateBuilder.js
Normal 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 },
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
190
backend/src/middleware/email/templates/emailVerification.html
Normal file
190
backend/src/middleware/email/templates/emailVerification.html
Normal 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 -->
|
||||||
11
backend/src/middleware/email/templates/index.js
Normal file
11
backend/src/middleware/email/templates/index.js
Normal 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')
|
||||||
256
backend/src/middleware/email/templates/layout.html
Normal file
256
backend/src/middleware/email/templates/layout.html
Normal 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>
|
||||||
@ -1,448 +1,189 @@
|
|||||||
<!DOCTYPE html>
|
<!-- Email Body German : BEGIN -->
|
||||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
|
<table class="email-german" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
|
||||||
xmlns:o="urn:schemas-microsoft-com:office:office">
|
style="margin: auto;">
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 20px 0; text-align: center">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<head>
|
<!-- Hero Image, Flush : BEGIN -->
|
||||||
<meta charset="utf-8">
|
<tr>
|
||||||
<meta name="viewport" content="width=device-width">
|
<td style="background-color: #ffffff;">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<img
|
||||||
<meta name="x-apple-disable-message-reformatting">
|
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"
|
||||||
<meta name="format-detection" content="telephone=no,address=no,email=no,date=no,url=no">
|
width="600" height="" alt="Human Connection community logo" border="0"
|
||||||
<title>Neues Passwort | Reset Password</title>
|
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]>
|
<!-- 1 Column Text + Button : BEGIN -->
|
||||||
<style>
|
<tr>
|
||||||
* {
|
<td style="background-color: #ffffff; padding: 0 20px;">
|
||||||
font-family: sans-serif !important;
|
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||||
}
|
|
||||||
</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;">
|
|
||||||
<tr>
|
<tr>
|
||||||
<td
|
<td
|
||||||
style="padding: 20px; font-family: Lato, sans-serif; font-size: 12px; line-height: 15px; text-align: center; color: #888888;">
|
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
|
||||||
<br><br>
|
<h1
|
||||||
Human Connection gGmbH<br><span class="unstyle-auto-detected-links">Bahnhofstraße 11, 73235 Weilheim /
|
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
|
||||||
Teck<br>Germany</span>
|
Hallo {{ name }}!</h1>
|
||||||
<br><br>
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<!-- Email Footer : END -->
|
</td>
|
||||||
|
</tr>
|
||||||
|
<!-- 1 Column Text + Button : END -->
|
||||||
|
|
||||||
<!--[if mso]>
|
<!-- 1 Column Text : BEGIN -->
|
||||||
</td>
|
<tr>
|
||||||
</tr>
|
<td style="background-color: #ffffff; padding: 0 20px;">
|
||||||
</table>
|
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||||
<![endif]-->
|
<tr>
|
||||||
</div>
|
<td
|
||||||
|
style="padding: 20px; padding-bottom: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
|
||||||
<!--[if mso | IE]>
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<![endif]-->
|
</td>
|
||||||
</center>
|
</tr>
|
||||||
</body>
|
<!-- 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 -->
|
||||||
|
|||||||
@ -1,485 +1,214 @@
|
|||||||
<!DOCTYPE html>
|
<!-- Email Body German : BEGIN -->
|
||||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
|
<table class="email-german" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
|
||||||
xmlns:o="urn:schemas-microsoft-com:office:office">
|
style="margin: auto;">
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 20px 0; text-align: center">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<head>
|
<!-- Hero Image, Flush : BEGIN -->
|
||||||
<meta charset="utf-8">
|
<tr>
|
||||||
<meta name="viewport" content="width=device-width">
|
<td style="background-color: #ffffff;">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<img
|
||||||
<meta name="x-apple-disable-message-reformatting">
|
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"
|
||||||
<meta name="format-detection" content="telephone=no,address=no,email=no,date=no,url=no">
|
width="600" height="" alt="Human Connection community logo" border="0"
|
||||||
<title>Willkommen, Bienvenue, Welcome to Human Connection</title>
|
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]>
|
<!-- 1 Column Text + Button : BEGIN -->
|
||||||
<style>
|
<tr>
|
||||||
* {
|
<td style="background-color: #ffffff; padding: 0 20px;">
|
||||||
font-family: sans-serif !important;
|
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||||
}
|
|
||||||
</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;">
|
|
||||||
‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ <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;">
|
|
||||||
<tr>
|
<tr>
|
||||||
<td
|
<td
|
||||||
style="padding: 20px; font-family: Lato, sans-serif; font-size: 12px; line-height: 15px; text-align: center; color: #888888;">
|
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
|
||||||
<br><br>
|
<h1
|
||||||
Human Connection gGmbH<br><span class="unstyle-auto-detected-links">Bahnhofstraße 11, 73235 Weilheim /
|
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
|
||||||
Teck<br>Germany</span>
|
Willkommen bei Human Connection!</h1>
|
||||||
<br><br>
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<!-- Email Footer : END -->
|
</td>
|
||||||
|
</tr>
|
||||||
|
<!-- 1 Column Text + Button : END -->
|
||||||
|
|
||||||
<!--[if mso]>
|
<!-- 1 Column Text : BEGIN -->
|
||||||
</td>
|
<tr>
|
||||||
</tr>
|
<td style="background-color: #ffffff; padding: 0 20px;">
|
||||||
</table>
|
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||||
<![endif]-->
|
<tr>
|
||||||
</div>
|
<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"
|
||||||
<!--[if mso | IE]>
|
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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<![endif]-->
|
</td>
|
||||||
</center>
|
</tr>
|
||||||
</body>
|
<!-- 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 -->
|
||||||
|
|||||||
@ -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 }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,448 +1,189 @@
|
|||||||
<!DOCTYPE html>
|
<!-- Email Body German : BEGIN -->
|
||||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
|
<table class="email-german" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
|
||||||
xmlns:o="urn:schemas-microsoft-com:office:office">
|
style="margin: auto;">
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 20px 0; text-align: center">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<head>
|
<!-- Hero Image, Flush : BEGIN -->
|
||||||
<meta charset="utf-8">
|
<tr>
|
||||||
<meta name="viewport" content="width=device-width">
|
<td style="background-color: #ffffff;">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<img
|
||||||
<meta name="x-apple-disable-message-reformatting">
|
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"
|
||||||
<meta name="format-detection" content="telephone=no,address=no,email=no,date=no,url=no">
|
width="600" height="" alt="Human Connection community logo" border="0"
|
||||||
<title>Falsche Mailadresse? | Wrong E-mail?</title>
|
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]>
|
<!-- 1 Column Text + Button : BEGIN -->
|
||||||
<style>
|
<tr>
|
||||||
* {
|
<td style="background-color: #ffffff; padding: 0 20px;">
|
||||||
font-family: sans-serif !important;
|
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||||
}
|
|
||||||
</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;">
|
|
||||||
<tr>
|
<tr>
|
||||||
<td
|
<td
|
||||||
style="padding: 20px; font-family: Lato, sans-serif; font-size: 12px; line-height: 15px; text-align: center; color: #888888;">
|
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
|
||||||
<br><br>
|
<h1
|
||||||
Human Connection gGmbH<br><span class="unstyle-auto-detected-links">Bahnhofstraße 11, 73235 Weilheim /
|
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
|
||||||
Teck<br>Germany</span>
|
Hallo!</h1>
|
||||||
<br><br>
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<!-- Email Footer : END -->
|
</td>
|
||||||
|
</tr>
|
||||||
|
<!-- 1 Column Text + Button : END -->
|
||||||
|
|
||||||
<!--[if mso]>
|
<!-- 1 Column Text : BEGIN -->
|
||||||
</td>
|
<tr>
|
||||||
</tr>
|
<td style="background-color: #ffffff; padding: 0 20px;">
|
||||||
</table>
|
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||||
<![endif]-->
|
<tr>
|
||||||
</div>
|
<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"
|
||||||
<!--[if mso | IE]>
|
style="color: #17b53e;">Human Connection</a> hast oder Dein Password gar nicht ändern willst,
|
||||||
|
kannst Du diese E-Mail einfach ignorieren!</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<![endif]-->
|
</td>
|
||||||
</center>
|
</tr>
|
||||||
</body>
|
<!-- 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 -->
|
||||||
|
|||||||
@ -170,6 +170,8 @@ const permissions = shield(
|
|||||||
block: isAuthenticated,
|
block: isAuthenticated,
|
||||||
unblock: isAuthenticated,
|
unblock: isAuthenticated,
|
||||||
markAsRead: isAuthenticated,
|
markAsRead: isAuthenticated,
|
||||||
|
AddEmailAddress: isAuthenticated,
|
||||||
|
VerifyEmailAddress: isAuthenticated,
|
||||||
},
|
},
|
||||||
User: {
|
User: {
|
||||||
email: isMyOwn,
|
email: isMyOwn,
|
||||||
|
|||||||
12
backend/src/models/UnverifiedEmailAddress.js
Normal file
12
backend/src/models/UnverifiedEmailAddress.js
Normal 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,
|
||||||
|
},
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ export default {
|
|||||||
User: require('./User.js'),
|
User: require('./User.js'),
|
||||||
InvitationCode: require('./InvitationCode.js'),
|
InvitationCode: require('./InvitationCode.js'),
|
||||||
EmailAddress: require('./EmailAddress.js'),
|
EmailAddress: require('./EmailAddress.js'),
|
||||||
|
UnverifiedEmailAddress: require('./UnverifiedEmailAddress.js'),
|
||||||
SocialMedia: require('./SocialMedia.js'),
|
SocialMedia: require('./SocialMedia.js'),
|
||||||
Post: require('./Post.js'),
|
Post: require('./Post.js'),
|
||||||
Comment: require('./Comment.js'),
|
Comment: require('./Comment.js'),
|
||||||
|
|||||||
92
backend/src/schema/resolvers/emails.js
Normal file
92
backend/src/schema/resolvers/emails.js
Normal 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'],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
298
backend/src/schema/resolvers/emails.spec.js
Normal file
298
backend/src/schema/resolvers/emails.spec.js
Normal 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.' }],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
26
backend/src/schema/resolvers/helpers/existingEmailAddress.js
Normal file
26
backend/src/schema/resolvers/helpers/existingEmailAddress.js
Normal 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
|
||||||
|
}
|
||||||
4
backend/src/schema/resolvers/helpers/generateNonce.js
Normal file
4
backend/src/schema/resolvers/helpers/generateNonce.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import uuid from 'uuid/v4'
|
||||||
|
export default function generateNonce() {
|
||||||
|
return uuid().substring(0, 6)
|
||||||
|
}
|
||||||
@ -1,41 +1,16 @@
|
|||||||
import { UserInputError } from 'apollo-server'
|
import { UserInputError } from 'apollo-server'
|
||||||
import uuid from 'uuid/v4'
|
|
||||||
import { neode } from '../../bootstrap/neo4j'
|
import { neode } from '../../bootstrap/neo4j'
|
||||||
import fileUpload from './fileUpload'
|
import fileUpload from './fileUpload'
|
||||||
import encryptPassword from '../../helpers/encryptPassword'
|
import encryptPassword from '../../helpers/encryptPassword'
|
||||||
|
import generateNonce from './helpers/generateNonce'
|
||||||
|
import existingEmailAddress from './helpers/existingEmailAddress'
|
||||||
|
|
||||||
const instance = neode()
|
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 {
|
export default {
|
||||||
Mutation: {
|
Mutation: {
|
||||||
CreateInvitationCode: async (_parent, args, context, _resolveInfo) => {
|
CreateInvitationCode: async (_parent, args, context, _resolveInfo) => {
|
||||||
args.token = uuid().substring(0, 6)
|
args.token = generateNonce()
|
||||||
const {
|
const {
|
||||||
user: { id: userId },
|
user: { id: userId },
|
||||||
} = context
|
} = context
|
||||||
@ -54,9 +29,9 @@ export default {
|
|||||||
return response
|
return response
|
||||||
},
|
},
|
||||||
Signup: async (_parent, args, context) => {
|
Signup: async (_parent, args, context) => {
|
||||||
const nonce = uuid().substring(0, 6)
|
const nonce = generateNonce()
|
||||||
args.nonce = nonce
|
args.nonce = nonce
|
||||||
let emailAddress = await alreadyExistingMail(_parent, args, context)
|
let emailAddress = await existingEmailAddress(_parent, args, context)
|
||||||
if (emailAddress) return emailAddress
|
if (emailAddress) return emailAddress
|
||||||
try {
|
try {
|
||||||
emailAddress = await instance.create('EmailAddress', args)
|
emailAddress = await instance.create('EmailAddress', args)
|
||||||
@ -67,9 +42,9 @@ export default {
|
|||||||
},
|
},
|
||||||
SignupByInvitation: async (_parent, args, context) => {
|
SignupByInvitation: async (_parent, args, context) => {
|
||||||
const { token } = args
|
const { token } = args
|
||||||
const nonce = uuid().substring(0, 6)
|
const nonce = generateNonce()
|
||||||
args.nonce = nonce
|
args.nonce = nonce
|
||||||
let emailAddress = await alreadyExistingMail(_parent, args, context)
|
let emailAddress = await existingEmailAddress(_parent, args, context)
|
||||||
if (emailAddress) return emailAddress
|
if (emailAddress) return emailAddress
|
||||||
try {
|
try {
|
||||||
const result = await instance.cypher(
|
const result = await instance.cypher(
|
||||||
|
|||||||
@ -257,7 +257,7 @@ describe('SignupByInvitation', () => {
|
|||||||
|
|
||||||
it('throws unique violation error', async () => {
|
it('throws unique violation error', async () => {
|
||||||
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
|
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 () => {
|
it('is allowed to signup users by email', async () => {
|
||||||
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
|
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
|
||||||
data: { Signup: { email: 'someuser@example.org' } },
|
data: { Signup: { email: 'someuser@example.org' } },
|
||||||
|
errors: undefined,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -342,7 +343,7 @@ describe('Signup', () => {
|
|||||||
|
|
||||||
it('throws UserInputError error because of unique constraint violation', async () => {
|
it('throws UserInputError error because of unique constraint violation', async () => {
|
||||||
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
|
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 () => {
|
it('resolves with the already existing email', async () => {
|
||||||
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
|
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
|
||||||
data: { Signup: { email: 'someuser@example.org' } },
|
data: { Signup: { email: 'someuser@example.org' } },
|
||||||
|
errors: undefined,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -359,6 +361,7 @@ describe('Signup', () => {
|
|||||||
await expect(neode.all('EmailAddress')).resolves.toHaveLength(2)
|
await expect(neode.all('EmailAddress')).resolves.toHaveLength(2)
|
||||||
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
|
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
|
||||||
data: { Signup: { email: 'someuser@example.org' } },
|
data: { Signup: { email: 'someuser@example.org' } },
|
||||||
|
errors: undefined,
|
||||||
})
|
})
|
||||||
await expect(neode.all('EmailAddress')).resolves.toHaveLength(2)
|
await expect(neode.all('EmailAddress')).resolves.toHaveLength(2)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -20,4 +20,9 @@ type Mutation {
|
|||||||
about: String
|
about: String
|
||||||
termsAndConditionsAgreedVersion: String!
|
termsAndConditionsAgreedVersion: String!
|
||||||
): User
|
): User
|
||||||
|
AddEmailAddress(email: String!): EmailAddress
|
||||||
|
VerifyEmailAddress(
|
||||||
|
nonce: String!
|
||||||
|
email: String!
|
||||||
|
): EmailAddress
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,21 @@
|
|||||||
import faker from 'faker'
|
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() {
|
export default function create() {
|
||||||
return {
|
return {
|
||||||
factory: async ({ args, neodeInstance }) => {
|
factory: async ({ args, neodeInstance }) => {
|
||||||
const defaults = {
|
args = defaults({ args })
|
||||||
email: faker.internet.email(),
|
|
||||||
verifiedAt: new Date().toISOString(),
|
|
||||||
}
|
|
||||||
args = {
|
|
||||||
...defaults,
|
|
||||||
...args,
|
|
||||||
}
|
|
||||||
return neodeInstance.create('EmailAddress', args)
|
return neodeInstance.create('EmailAddress', args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import createTag from './tags.js'
|
|||||||
import createSocialMedia from './socialMedia.js'
|
import createSocialMedia from './socialMedia.js'
|
||||||
import createLocation from './locations.js'
|
import createLocation from './locations.js'
|
||||||
import createEmailAddress from './emailAddresses.js'
|
import createEmailAddress from './emailAddresses.js'
|
||||||
|
import createUnverifiedEmailAddresss from './unverifiedEmailAddresses.js'
|
||||||
|
|
||||||
export const seedServerHost = 'http://127.0.0.1:4001'
|
export const seedServerHost = 'http://127.0.0.1:4001'
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ const factories = {
|
|||||||
SocialMedia: createSocialMedia,
|
SocialMedia: createSocialMedia,
|
||||||
Location: createLocation,
|
Location: createLocation,
|
||||||
EmailAddress: createEmailAddress,
|
EmailAddress: createEmailAddress,
|
||||||
|
UnverifiedEmailAddress: createUnverifiedEmailAddresss,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const cleanDatabase = async (options = {}) => {
|
export const cleanDatabase = async (options = {}) => {
|
||||||
|
|||||||
10
backend/src/seed/factories/unverifiedEmailAddresses.js
Normal file
10
backend/src/seed/factories/unverifiedEmailAddresses.js
Normal 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)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -115,7 +115,7 @@ describe('Signup', () => {
|
|||||||
mocks.$apollo.mutate = jest
|
mocks.$apollo.mutate = jest
|
||||||
.fn()
|
.fn()
|
||||||
.mockRejectedValue(
|
.mockRejectedValue(
|
||||||
new Error('UserInputError: User account with this email already exists.'),
|
new Error('UserInputError: A user account with this email already exists.'),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -128,7 +128,7 @@ export default {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
const { message } = err
|
const { message } = err
|
||||||
const mapping = {
|
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',
|
'Invitation code already used or does not exist': 'invalid-invitation-token',
|
||||||
}
|
}
|
||||||
for (const [pattern, key] of Object.entries(mapping)) {
|
for (const [pattern, key] of Object.entries(mapping)) {
|
||||||
|
|||||||
20
webapp/graphql/EmailAddress.js
Normal file
20
webapp/graphql/EmailAddress.js
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
@ -158,6 +158,27 @@
|
|||||||
"labelBio": "Über dich",
|
"labelBio": "Über dich",
|
||||||
"success": "Deine Daten wurden erfolgreich aktualisiert!"
|
"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": {
|
"validation": {
|
||||||
"slug": {
|
"slug": {
|
||||||
"regex": "Es sind nur Kleinbuchstaben, Zahlen, Unterstriche oder Bindestriche erlaubt.",
|
"regex": "Es sind nur Kleinbuchstaben, Zahlen, Unterstriche oder Bindestriche erlaubt.",
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
"maintenance": {
|
"maintenance": {
|
||||||
"title": "Human Connection is under maintenance",
|
"title": "Human Connection is under maintenance",
|
||||||
"explanation": "At the moment we are doing some scheduled maintenance, please try again later.",
|
"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": {
|
"index": {
|
||||||
"no-results": "No contributions found.",
|
"no-results": "No contributions found.",
|
||||||
@ -159,6 +159,27 @@
|
|||||||
"labelBio": "About You",
|
"labelBio": "About You",
|
||||||
"success": "Your data was successfully updated!"
|
"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": {
|
"validation": {
|
||||||
"slug": {
|
"slug": {
|
||||||
"regex": "Allowed characters are only lowercase letters, numbers, underscores and hyphens.",
|
"regex": "Allowed characters are only lowercase letters, numbers, underscores and hyphens.",
|
||||||
@ -254,7 +275,7 @@
|
|||||||
"users": {
|
"users": {
|
||||||
"name": "Users",
|
"name": "Users",
|
||||||
"form": {
|
"form": {
|
||||||
"placeholder": "E-Mail, name or description"
|
"placeholder": "email, name or description"
|
||||||
},
|
},
|
||||||
"table": {
|
"table": {
|
||||||
"columns": {
|
"columns": {
|
||||||
|
|||||||
@ -23,6 +23,10 @@ export default {
|
|||||||
name: this.$t('settings.data.name'),
|
name: this.$t('settings.data.name'),
|
||||||
path: `/settings`,
|
path: `/settings`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: this.$t('settings.email.name'),
|
||||||
|
path: `/settings/my-email-address`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: this.$t('settings.security.name'),
|
name: this.$t('settings.security.name'),
|
||||||
path: `/settings/security`,
|
path: `/settings/security`,
|
||||||
|
|||||||
@ -31,14 +31,7 @@
|
|||||||
:placeholder="$t('settings.data.labelBio')"
|
:placeholder="$t('settings.data.labelBio')"
|
||||||
/>
|
/>
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
<ds-button
|
<ds-button icon="check" :disabled="errors" type="submit" :loading="loadingData" primary>
|
||||||
style="float: right;"
|
|
||||||
icon="check"
|
|
||||||
:disabled="errors"
|
|
||||||
type="submit"
|
|
||||||
:loading="loadingData"
|
|
||||||
primary
|
|
||||||
>
|
|
||||||
{{ $t('actions.save') }}
|
{{ $t('actions.save') }}
|
||||||
</ds-button>
|
</ds-button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
53
webapp/pages/settings/my-email-address/enter-nonce.spec.js
Normal file
53
webapp/pages/settings/my-email-address/enter-nonce.spec.js
Normal 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()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
59
webapp/pages/settings/my-email-address/enter-nonce.vue
Normal file
59
webapp/pages/settings/my-email-address/enter-nonce.vue
Normal 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>
|
||||||
116
webapp/pages/settings/my-email-address/index.spec.js
Normal file
116
webapp/pages/settings/my-email-address/index.spec.js
Normal 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')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
113
webapp/pages/settings/my-email-address/index.vue
Normal file
113
webapp/pages/settings/my-email-address/index.vue
Normal 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>
|
||||||
164
webapp/pages/settings/my-email-address/verify.spec.js
Normal file
164
webapp/pages/settings/my-email-address/verify.spec.js
Normal 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()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
99
webapp/pages/settings/my-email-address/verify.vue
Normal file
99
webapp/pages/settings/my-email-address/verify.vue
Normal 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>
|
||||||
Loading…
x
Reference in New Issue
Block a user