206 lines
7.7 KiB
Elixir
206 lines
7.7 KiB
Elixir
defmodule WhoNeedHelp.Accounts.UserNotifier do
|
|
use Gettext, backend: WhoNeedHelpWeb.Gettext
|
|
|
|
import Swoosh.Email
|
|
|
|
alias WhoNeedHelp.Accounts.User
|
|
alias WhoNeedHelp.EmailDelivery
|
|
|
|
# Delivers the email using the application mailer.
|
|
defp deliver(recipient, subject, text_body, html_body, kind) do
|
|
from = Application.fetch_env!(:who_need_help, :mailer_from)
|
|
|
|
email =
|
|
new()
|
|
|> to(recipient)
|
|
|> from({from[:name], from[:address]})
|
|
|> subject(subject)
|
|
|> text_body(text_body)
|
|
|> html_body(html_body)
|
|
|
|
with {:ok, _metadata} <- EmailDelivery.deliver(email, kind) do
|
|
{:ok, email}
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Deliver instructions to update a user email.
|
|
"""
|
|
def deliver_update_email_instructions(user, url) do
|
|
with_user_locale(user, fn ->
|
|
deliver_action_email(user,
|
|
kind: :auth_email_change,
|
|
subject: gettext("Confirm your Who Need Help email change"),
|
|
heading: gettext("Confirm your new email address"),
|
|
introduction:
|
|
gettext("Use the secure link below to confirm this email address for your account."),
|
|
action_label: gettext("Confirm email address"),
|
|
url: url,
|
|
expiry_note: gettext("This confirmation link expires in 7 days."),
|
|
security_note:
|
|
gettext(
|
|
"If you did not request this change, ignore this email. Your address will not change."
|
|
)
|
|
)
|
|
end)
|
|
end
|
|
|
|
@doc """
|
|
Deliver instructions to log in with a magic link.
|
|
"""
|
|
def deliver_login_instructions(user, url) do
|
|
case user do
|
|
%User{confirmed_at: nil} -> deliver_confirmation_instructions(user, url)
|
|
_ -> deliver_magic_link_instructions(user, url)
|
|
end
|
|
end
|
|
|
|
@doc "Delivers instructions for verifying a local account before connecting Google sign-in."
|
|
def deliver_google_link_instructions(user, url) do
|
|
with_user_locale(user, fn ->
|
|
deliver_action_email(user,
|
|
kind: :auth_google_link,
|
|
subject: gettext("Confirm Google sign-in for Who Need Help"),
|
|
heading: gettext("Confirm Google sign-in"),
|
|
introduction:
|
|
gettext("Use the secure link below to sign in and connect Google to your account."),
|
|
action_label: gettext("Confirm Google sign-in"),
|
|
url: url,
|
|
expiry_note: gettext("This one-time link expires in 15 minutes."),
|
|
security_note:
|
|
gettext("If you did not request this, ignore this email. Google will not be connected.")
|
|
)
|
|
end)
|
|
end
|
|
|
|
defp deliver_magic_link_instructions(user, url) do
|
|
with_user_locale(user, fn ->
|
|
deliver_action_email(user,
|
|
kind: :auth_login,
|
|
subject: gettext("Your Who Need Help sign-in link"),
|
|
heading: gettext("Sign in to Who Need Help"),
|
|
introduction: gettext("Use the secure link below to sign in to your account."),
|
|
action_label: gettext("Sign in to Who Need Help"),
|
|
url: url,
|
|
expiry_note: gettext("This one-time link expires in 15 minutes."),
|
|
security_note:
|
|
gettext("If you did not request this sign-in, you can safely ignore this email.")
|
|
)
|
|
end)
|
|
end
|
|
|
|
defp deliver_confirmation_instructions(user, url) do
|
|
with_user_locale(user, fn ->
|
|
deliver_action_email(user,
|
|
kind: :auth_registration,
|
|
subject: gettext("Confirm your Who Need Help account"),
|
|
heading: gettext("Confirm your account"),
|
|
introduction: gettext("Use the secure link below to confirm your Who Need Help account."),
|
|
action_label: gettext("Confirm account"),
|
|
url: url,
|
|
expiry_note: gettext("This one-time link expires in 15 minutes."),
|
|
security_note:
|
|
gettext("If you did not create this account, you can safely ignore this email.")
|
|
)
|
|
end)
|
|
end
|
|
|
|
defp deliver_action_email(user, content) do
|
|
kind = Keyword.fetch!(content, :kind)
|
|
subject = Keyword.fetch!(content, :subject)
|
|
heading = Keyword.fetch!(content, :heading)
|
|
introduction = Keyword.fetch!(content, :introduction)
|
|
action_label = Keyword.fetch!(content, :action_label)
|
|
url = Keyword.fetch!(content, :url)
|
|
expiry_note = Keyword.fetch!(content, :expiry_note)
|
|
security_note = Keyword.fetch!(content, :security_note)
|
|
|
|
text = """
|
|
#{heading}
|
|
|
|
#{introduction}
|
|
|
|
#{action_label}: #{url}
|
|
|
|
#{expiry_note}
|
|
|
|
#{security_note}
|
|
|
|
Who Need Help
|
|
"""
|
|
|
|
html = action_email_html(heading, introduction, action_label, url, expiry_note, security_note)
|
|
|
|
deliver(user.email, subject, text, html, kind)
|
|
end
|
|
|
|
defp action_email_html(heading, introduction, action_label, url, expiry_note, security_note) do
|
|
escaped_heading = escape_html(heading)
|
|
escaped_introduction = escape_html(introduction)
|
|
escaped_action_label = escape_html(action_label)
|
|
escaped_url = escape_html(url)
|
|
escaped_expiry_note = escape_html(expiry_note)
|
|
escaped_security_note = escape_html(security_note)
|
|
|
|
"""
|
|
<!doctype html>
|
|
<html lang="#{escape_html(Gettext.get_locale(WhoNeedHelpWeb.Gettext))}">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="color-scheme" content="light">
|
|
<title>#{escaped_heading}</title>
|
|
</head>
|
|
<body style="margin:0;padding:0;background:#f5f7f6;color:#1d1d20;font-family:Arial,Helvetica,sans-serif;">
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#f5f7f6;">
|
|
<tr>
|
|
<td align="center" style="padding:32px 16px;">
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="max-width:560px;background:#ffffff;border:1px solid #dfe5e2;border-radius:16px;">
|
|
<tr>
|
|
<td style="padding:28px 32px 12px;font-size:18px;font-weight:700;color:#007d6b;">Who Need Help</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding:8px 32px 0;">
|
|
<h1 style="margin:0;font-size:26px;line-height:1.25;color:#1d1d20;">#{escaped_heading}</h1>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding:16px 32px 0;font-size:16px;line-height:1.6;color:#555b58;">#{escaped_introduction}</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding:24px 32px;">
|
|
<a href="#{escaped_url}" style="display:inline-block;padding:13px 20px;border-radius:8px;background:#a93612;color:#ffffff;text-decoration:none;font-size:16px;font-weight:700;">#{escaped_action_label}</a>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding:0 32px 8px;font-size:14px;line-height:1.55;color:#555b58;">#{escaped_expiry_note}</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding:8px 32px 0;font-size:14px;line-height:1.55;color:#555b58;">#{escaped_security_note}</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding:20px 32px 8px;font-size:12px;line-height:1.5;color:#747a77;">#{escape_html(gettext("If the button does not work, copy and paste this address into your browser:"))}</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding:0 32px 28px;font-size:12px;line-height:1.5;word-break:break-all;"><a href="#{escaped_url}" style="color:#007d6b;">#{escaped_url}</a></td>
|
|
</tr>
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</body>
|
|
</html>
|
|
"""
|
|
end
|
|
|
|
defp escape_html(value) do
|
|
value
|
|
|> Phoenix.HTML.html_escape()
|
|
|> Phoenix.HTML.safe_to_string()
|
|
end
|
|
|
|
defp with_user_locale(%User{locale: locale}, fun) do
|
|
Gettext.with_locale(WhoNeedHelpWeb.Gettext, WhoNeedHelp.Locales.normalize(locale), fun)
|
|
end
|
|
end
|