217 lines
6.0 KiB
Elixir
217 lines
6.0 KiB
Elixir
defmodule Mobilizon.Web.Email.User do
|
|
@moduledoc """
|
|
Handles emails sent to users.
|
|
"""
|
|
|
|
use Phoenix.Swoosh, view: Mobilizon.Web.EmailView
|
|
|
|
import Mobilizon.Web.Gettext, only: [gettext: 2]
|
|
|
|
alias Mobilizon.{Config, Crypto, Users}
|
|
alias Mobilizon.Storage.Repo
|
|
alias Mobilizon.Users.User
|
|
|
|
alias Mobilizon.Web.Email
|
|
|
|
require Logger
|
|
|
|
@spec confirmation_email(User.t(), String.t()) :: Swoosh.Email.t()
|
|
def confirmation_email(
|
|
%User{email: email, confirmation_token: confirmation_token},
|
|
locale \\ "en"
|
|
) do
|
|
Gettext.put_locale(locale)
|
|
|
|
subject =
|
|
gettext(
|
|
"Instructions to confirm your Mobilizon account on %{instance}",
|
|
instance: Config.instance_name()
|
|
)
|
|
|
|
[to: email, subject: subject]
|
|
|> Email.base_email()
|
|
|> render_body(:registration_confirmation, %{
|
|
locale: locale,
|
|
token: confirmation_token,
|
|
subject: subject,
|
|
offer_unsupscription: false
|
|
})
|
|
end
|
|
|
|
@spec reset_password_email(User.t(), String.t()) :: Swoosh.Email.t()
|
|
def reset_password_email(
|
|
%User{email: email, reset_password_token: reset_password_token},
|
|
locale \\ "en"
|
|
) do
|
|
Gettext.put_locale(locale)
|
|
|
|
subject =
|
|
gettext(
|
|
"Instructions to reset your password on %{instance}",
|
|
instance: Config.instance_name()
|
|
)
|
|
|
|
[to: email, subject: subject]
|
|
|> Email.base_email()
|
|
|> render_body(:password_reset, %{
|
|
locale: locale,
|
|
token: reset_password_token,
|
|
subject: subject,
|
|
offer_unsupscription: false
|
|
})
|
|
end
|
|
|
|
@spec check_confirmation_token(String.t()) ::
|
|
{:ok, User.t()} | {:error, :invalid_token | Ecto.Changeset.t()}
|
|
def check_confirmation_token(token) when is_binary(token) do
|
|
case Users.get_user_by_activation_token(token) do
|
|
%User{} = user ->
|
|
case Users.update_user(user, %{
|
|
confirmed_at: DateTime.utc_now() |> DateTime.truncate(:second),
|
|
confirmation_sent_at: nil,
|
|
confirmation_token: nil,
|
|
email: user.unconfirmed_email || user.email
|
|
}) do
|
|
{:ok, %User{} = user} ->
|
|
Logger.info("User #{user.email} has been confirmed")
|
|
{:ok, user}
|
|
|
|
{:error, %Ecto.Changeset{} = err} ->
|
|
{:error, err}
|
|
end
|
|
|
|
nil ->
|
|
{:error, :invalid_token}
|
|
end
|
|
end
|
|
|
|
def resend_confirmation_email(%User{} = user, locale \\ "en") do
|
|
with :ok <- we_can_send_email(user, :confirmation_sent_at),
|
|
{:ok, user} <-
|
|
Users.update_user(user, %{
|
|
"confirmation_sent_at" => DateTime.utc_now() |> DateTime.truncate(:second)
|
|
}) do
|
|
send_confirmation_email(user, locale)
|
|
Logger.info("Sent confirmation email again to #{user.email}")
|
|
{:ok, user.email}
|
|
end
|
|
end
|
|
|
|
@spec send_confirmation_email(User.t(), String.t()) :: Swoosh.Email.t()
|
|
def send_confirmation_email(%User{} = user, locale \\ "en") do
|
|
user
|
|
|> Email.User.confirmation_email(locale)
|
|
|> Email.Mailer.send_email()
|
|
end
|
|
|
|
@doc """
|
|
Check that the provided token is correct and update provided password
|
|
"""
|
|
@spec check_reset_password_token(String.t(), String.t()) ::
|
|
{:ok, User.t()} | {:error, :user_not_found | Ecto.Changeset.t()}
|
|
def check_reset_password_token(password, token) do
|
|
case Users.get_user_by_reset_password_token(token) do
|
|
%User{} = user ->
|
|
user
|
|
|> User.password_reset_changeset(%{
|
|
"password" => password,
|
|
"reset_password_sent_at" => nil,
|
|
"reset_password_token" => nil
|
|
})
|
|
|> Repo.update()
|
|
|
|
nil ->
|
|
{:error, :user_not_found}
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Send the email reset password, if it's not too soon since the last send
|
|
"""
|
|
@spec send_password_reset_email(User.t(), String.t()) :: tuple
|
|
def send_password_reset_email(%User{} = user, locale \\ "en") do
|
|
with :ok <- we_can_send_email(user, :reset_password_sent_at),
|
|
{:ok, %User{} = user_updated} <-
|
|
Repo.update(
|
|
User.send_password_reset_changeset(user, %{
|
|
"reset_password_token" => Crypto.random_string(30),
|
|
"reset_password_sent_at" => DateTime.utc_now() |> DateTime.truncate(:second)
|
|
})
|
|
) do
|
|
user_updated
|
|
|> Email.User.reset_password_email(locale)
|
|
|> Email.Mailer.send_email()
|
|
|
|
{:ok, user.email}
|
|
else
|
|
{:error, reason} -> {:error, reason}
|
|
end
|
|
end
|
|
|
|
def send_email_reset_old_email(%User{
|
|
locale: user_locale,
|
|
email: email,
|
|
unconfirmed_email: unconfirmed_email
|
|
}) do
|
|
Gettext.put_locale(user_locale)
|
|
|
|
subject =
|
|
gettext(
|
|
"Mobilizon on %{instance}: email changed",
|
|
instance: Config.instance_name()
|
|
)
|
|
|
|
[to: email, subject: subject]
|
|
|> Email.base_email()
|
|
|> render_body(:email_changed_old, %{
|
|
locale: user_locale,
|
|
new_email: unconfirmed_email,
|
|
subject: subject,
|
|
offer_unsupscription: false
|
|
})
|
|
end
|
|
|
|
def send_email_reset_new_email(%User{
|
|
locale: user_locale,
|
|
unconfirmed_email: unconfirmed_email,
|
|
confirmation_token: confirmation_token
|
|
}) do
|
|
Gettext.put_locale(user_locale)
|
|
|
|
subject =
|
|
gettext(
|
|
"Mobilizon on %{instance}: confirm your email address",
|
|
instance: Config.instance_name()
|
|
)
|
|
|
|
[to: unconfirmed_email, subject: subject]
|
|
|> Email.base_email()
|
|
|> render_body(:email_changed_new, %{
|
|
locale: user_locale,
|
|
token: confirmation_token,
|
|
subject: subject,
|
|
offer_unsupscription: false
|
|
})
|
|
end
|
|
|
|
@spec we_can_send_email(User.t(), atom) :: :ok | {:error, :email_too_soon}
|
|
defp we_can_send_email(%User{} = user, key) do
|
|
case Map.get(user, key) do
|
|
nil ->
|
|
:ok
|
|
|
|
_ ->
|
|
case DateTime.compare(
|
|
DateTime.add(Map.get(user, key), 3600),
|
|
DateTime.utc_now() |> DateTime.truncate(:second)
|
|
) do
|
|
:lt ->
|
|
:ok
|
|
|
|
_ ->
|
|
{:error, :email_too_soon}
|
|
end
|
|
end
|
|
end
|
|
end
|